s3lph
s3lph

Reputation: 4655

ArrayList gets manipulated by called method

I'm currently having problems with the following, using Java 8:

I want to pass an ArrayList<String> as argument to another method. This method, among other things, removes an object from the ArrayList it was given:

public static void calledMethod(String item, ArrayList<String> list) {
    list.remove(item);
}

Now I tried calling this method like this from main:

ArrayList<String> list = new ArrayList<String>();
list.add("abc");
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
    String item = itr.next();  //<-
    calledMethod(item, list);
}

In this case, the JVM returns a java.util.ConcurrentModificationException at the marked line in the code above (//<-). As far as my knowledge goes, this means that the list was modified while it was iterated. But how can this happen? As far as I know, Java hands method arguments by value, not by reference. If I call calledMethod using the following code in my main method, no error occurs:

ArrayList<String> list = new ArrayList<String>();
list.add("abc");
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
    String item = itr.next();  //<-
    calledMethod(item, (ArrayList<String>) list.clone());
}

So passing a cloned object to calledMethod works. What is wrong here?


java -version: Java SE Runtime Environment (build 1.8.0_20-b26)

I'm using Oracle Java on a Linux Mint 64bit.

Upvotes: 0

Views: 237

Answers (3)

Joel
Joel

Reputation: 2404

What happens here is that your iterator got "lost" due to "concurrent" modification on the list (it's not actually concurrent, but it's a modification made on the list without notifying the iterator. So from Iterator point of view, it's as if the list was modified at the same time)

While iterating through a list, you should never remove an element with List.remove. Use Iterator.remove instead, this way the iterator won't have an invalid state due to removing:

Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
    String item = itr.next();
    itr.remove(); // will remove "item" and keep iterator in a correct state
}

Upvotes: 1

Juru
Juru

Reputation: 1629

Yes you pass it by value, but you are passing an object which is actually passing the reference of the object in that case. Thus you are still modifying the actualy list in the calledMethod.

Upvotes: 0

rgettman
rgettman

Reputation: 178253

Yes, Java is pass by value. But what is list? It's a reference to the actual ArrayList object. When it's passed to the method, it's copied, but the copy still refers to the same object, so the method's modification is visible to the calling method, and a ConcurrentModificationException occurs.

When you clone the object, only then is a copy of the object made, preventing the ConcurrentModificationException.

Upvotes: 5

Related Questions