1

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

4 Answers 4

2

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>>).

Sign up to request clarification or add additional context in comments.

Comments

1

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)));

4 Comments

The problem is that I need to pass b as an ArrayList<ArrayList<Object>> after I finish inserting in it, and from what I've read it is not possible without manually copying each and every item.
@nigod, well, it's not a good design solution if you are supposed to pass an ArrayList<ArrayList<Object>> as a parameter, right?
I can not change the parameter's type :(
I changed the whole approach to the problem, I found something more intelligible. thanks for your help
1

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);

2 Comments

ok, but then how will I be able to convert b to ArrayList<ArrayList<Object>> ?
@Egor wanted to post the same thing, saw your answer, clicked your profile, same city :) u get un upvote for that
0

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.

5 Comments

but I still need to return b as ArrayList<ArrayList<Object>>, what should I do? So basically I have a ArrayList<ArrayList<Object>> intended to be thread-safe and I try some sort of conversions to make that possible.
@nigod Why do you need that the objects are ArrayList? Note that Collections#synchronizeList just wraps the given list. All accesses will in the end pass the calls of the original list. So if you are interested on RandomAccess (which ArrayList has) note that the List returned by the method will also have RandomAccess. Indeed it even implements RandomAccess in that case and you can use list instanceof RandomAccess to check for that.
See the implementation of this method.
You won't be able to maintain ArrayList<ArrayList<Object>> and also synchronize it. The alternative would be to create a class SynchronizeManager which maintains this object internally and all accesses you want to make you do through this class. Then you internally can have this object but all accesses, if done through SynchronizeManager could be made synchronized. But honestly, I think the best would be to work on List<List<Object>> by using Collections#synchronizeList.
Or are you asking for converting it once to a synchronized version, the doing some heavy computations and after that converting it back? I mean the runtime won't be good since you need to build the resulting structure from ground up (copy everything) but if you only do it once it won't matter that much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.