Reputation: 13
I know that ArrayList
is not thread safe and I've seen many people recommending
converting it in (Collections.synchronizedList)
. I need to create a system where I convert my ArrayList
to (Collections.synchronizedList)
and then back to ArrayList
. But first I need to insert a
in b
.
The problem is that I do not know how to convert from (Collections.synchronizedList)
to ArrayList
and I get an error when I try to insert the following element:
public List<Object> a;
public List<ArrayList<Object>> b;
a = Collections.synchronizedList(new ArrayList<Object>());
b = Collections.synchronizedList(b);
When I try the following command an exception is thrown:
b.add(id, (ArrayList<Object>) a);
java.base/java.util.Collections$SynchronizedRandomAccessList cannot be cast to java.base/java.util.ArrayList
Upvotes: 1
Views: 2464
Reputation: 299048
a) Careful, Collections.synchronizedList() does not make your list entirely thread safe, it just synchronizes your operations. If you have multiple threads reading and writing your list, you may still run into ConcurrentModificationException if you don't know what you are doing. If you want better thread safety, use CopyOnWriteArrayList
. If, for example, one thread adds items to a List, while another iterates, that will break any List implementation except CopyOnWriteArrayList, whether or not it's wrapped in Collections.synchronizedList().
b) Collections.synchronizedList() is a decorator around your original list. I.e. your ArrayList is still there, inside the decorator. You don't need to copy it back, just keep a reference to the original ArrayList variable. Every change you made to the synchronized List got propagated to the ArrayList. On the other hand: why do you need an ArrayList? A good api will accept any List type. Relying on ArrayList as implementation type violates at least one Effective Java rule (Item 52: Refer to objects by their interfaces). So unless you code against an API that explicitly requires ArrayList, just use your synchronized list (change your field type to List<List<Object>>
).
Upvotes: 2
Reputation: 49646
You'd better change the b
variable to:
public List<List<Object>> b;
which doesn't depend on a concrete implementation. It will make possible to pass any List
implementation you want - either an ArrayList
or a Collections.SynchronizedList
.
Otherwise, if you don't want to change the field type (I wouldn't recommend doing so), you could use either a copy constructor:
b.add(2, new ArrayList<>(a));
or a Stream API approach:
b.add(2, a.stream().collect(Collectors.toCollection(ArrayList::new)));
Upvotes: 1
Reputation: 25903
It's simple. Your b
variable is of type:
List<ArrayList<Object>> b
So it contains elements of type ArrayList<Object>
and not List<Object>
. Thus the compiler complaints when you try to insert a
since a
is not of type ArrayList<Object>
(it never was though). You have created it by using
a = Collections.synchronizedList(...);
The method Collections#synchronizedList
does not return an ArrayList
, it returns a List
.
Change your b
object to
List<List<Object>> b
and you will be able to insert a
.
Alternatively you would need to explicitly convert a
to ArrayList
again. For example by using.
ArrayList<Object> resultA = new ArrayList<>(a);
and then inserting resultA
. The given constructor creates a new ArrayList
which adds all elements of the given collection.
Upvotes: 0
Reputation: 40218
There's an ArrayList
constructor that takes a Collection
:
ArrayList<Object> arrayList = new ArrayList<>(a);
However, it might be more reasonable to change the type of b
to List<List<Object>>
:
public List<ArrayList<Object>> b;
b.add(id, a);
Upvotes: 1