nigod
nigod

Reputation: 13

Converting ArrayList to Collections.synchronizedList

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

Answers (4)

Sean Patrick Floyd
Sean Patrick Floyd

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

Andrew
Andrew

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

Zabuzard
Zabuzard

Reputation: 25903

Explanation

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.


Solution

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

Egor
Egor

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

Related Questions