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.
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.
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
}
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.
Related
I am having the following problem:
Given an ArrayList (let's call it list), how can I "double-iterate" through it without getting ConcurrentModificationException?
Here's what I've tried:
iterator out = list.iterator();
iterator in;
while(out.hasNext()){
...
in = list.iterator();
while(in.hasNext()){
...
if(something)
in.remove();
}
You can't do that. A potential solution might be to mark objects to be removed, for example:
final List<Foo> toRemove = new ArrayList<>();
for (Foo a : list)
{
for (Foo b : list)
{
if (something)
{
toRemove.add(b);
}
}
}
list.removeAll(toRemove);
You may need some additional checks to see that the object isn't already marked for removal. It's impossible to say given how vague your example is.
You are trying to modify an iterator. It will give you concurrentModification exception.
In java 8 you can easily remove it using
list.removeIf(someCondition)
Try this link java8 collections
The Iterator instance provided through a call to List#iterator method preserves a count scalar allowing to detect external changes to the Collection container.
When an element is removed from the collection by any other mean than going through the same Iterator#remove(T) call, the count is not updated behind the scenes.
Therefore when you request for the #next() element through the iterator instance, the count is checked against an expected value and if both values does not match (since an element has been removed through another iterator) a ConcurrentModificationException is thrown (even though you may be working in a single threaded environment).
The solution whould be, as #Michael stated, to keep track of the container elements that should be removed then perform a bulk delete:
Collection<Object> temp = new ArrayList<>();
iterator out = list.iterator();
iterator in;
while (out.hasNext()) {
// ...
in = list.iterator();
while (in.hasNext()) {
// ...
if(something)
// just mark the element for deletion
temp.add(in.next());
}
}
// delete all the obsolete elements
list.removeAll(temp);
In collection once iterator creator If you try to modify the content not through same iterator it will throw concurrent exception.If you required some special kind of iterator then you can go ahead and implement your own.
I have a code that adds data to a list. What I do not understand is why
the UnsupportedOperationException is thrown in one case and
ConcurrentModificationException in the other.
I am adding data in list in both the case and then trying to remove list
data while iterating over the list.
What i have read so far is that whenever any modification is made to
fail- fast collection,ConcurrentModificationException is thrown. So why this
different behavior in both these cases?
List<String> animalList = new ArrayList<>();
animalList.add("cat");
animalList.add("dog");
animalList.add("bear");
animalList.add("lion");
Iterator<String> iter = animalList.iterator();
while(iter.hasNext()){
String animal = iter.next();
System.out.println(animal);
animalList.remove(3);
}
This code throws ConcurrentModificationException
String[] strings = { "Java", "Honk", "Test" };
List<String> list = Arrays.asList(strings);
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String name = iterator.next();
System.out.println(name);
list.remove(3);
}
while this one throws UnsupportedOperationException
For the code block, where you get ConcurrentModificationException , you get that exception because you created an iterator on List then removing directly from list from within loop so iterator has issues. You should use remove() method of iterator itself - Iterator.remove().
You should directly remove an element from list when removing from outside iterator loop. See this another SO Question
In second case, with Arrays.asList , you get a List but actual list object might not be an ArrayList and remove(int index) operation is optional at List interface. See this
All in all, as far as UnsupportedOperationException is concerned, in first case you are guaranteed to working with an ArrayList and for that class, remove operation is supported , See this
For second case, you can't be so sure. Refer documentation of Arrays.asList where it says that returned list of fixed size so certain operations are not supported.
Arrays.asList does not return an ArrayList. In fact, the list returned is not modifiable, thus when you try to modify it, it throws the UnsupportedOperationException.
This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 7 years ago.
I've stumbled upon this code, which throws a ConcurrentModificationException
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
for (String s : list) {
if (s.equals("a"))
list.remove(s);
}
If you add an Iterator and use a while-loop, the code works fine:
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String s = iter.next();
if (s.equals("a")) {
iter.remove();
}
}
I dont understand, why it is necessary, to use Iterator<String> in this case.
Is it because of the ArrayList not having some sort of ability to get iterated, altough it is a subclass of Collection?
Is it necessary to use the while-loop, or could you produce a version with a for-loop?
If a Collection is modified while iterating over it, in most of the implementations, a ConcurrentModificationException is thrown.
The "foreach" version:
for (String s : list) {
if (s.equals("a"))
list.remove(s);
}
internally is equivalent to
for(Iterator<String> i = list.iterator(); i.hasNext(); ) {
String s = i.next();
if (s.equals("a"))
list.remove(s);
}
As you can see, an interator is created, but the list is modified directly. The list can only be modified by the iterator i used to iterate over it.
Your link actually explains it pretty well:
In the foreach loop, compiler will make the .next() called after the operation of removing element, which caused the ConcurrentModificationException.
The for uses an iterator internally. But you delete on the list directly => CME. The internal call to next after the deletion will throw it because it finds the list modified.
In the example with while, you delete through the iterator, which works fine.
When removing object from collections, use ITERATORS
Your code does not work because you are modifying the collection while looping over it. According to Oracle's javadoc the only safe way to do it is using iterators.
Note that Iterator.remove() is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.
Have a look at this for further infos
So I have a variable string and I want to store it inside an Iterator of type string, but I keep getting an error stating that it is incompatible types. How can I store a string variable inside an iterator?
Incompatible Types:
Required: java.util.Iterator <java.lang.String>
Found: java.lang.String
This is what I've done so far:
Iterator<?> perEntry = entries.iterator();
Iterator<String> ids;
while (perEntry.hasNext()) {
ids = perEntry.next().getId();
}
Any help would be appreciated thank you!
P.S. I forgot to include this, but how do I return this Iterator? I get an error stating that it found java.util.ObjectType, and it requires java.util.Iterator.
You misunderstood iterators. An iterator is not equivalent to an object in a collection being iterated. Instead, it acts like a "pointer" to such an object.
If you have Iterator<Entity>, you can get an Entity out of it by calling next(). However, an iterator itself would remain an iterator on Entity. It cannot be converted into an iterator of String.
However, you can harvest all strings from an iterator into a collection, like this:
List<String> idList = new ArrayList<>();
while (perEntry.hasNext()) {
idList.add(perEntry.next().getId());
}
Once you have your idList list filled with data, you can get its iterator. It would be an Iterator<String>, because idList element is of type String:
Iterator<String> ids = idList.iterator();
In Java, when you iterator over a Vector<String[]>, why is .next() an Object that needs to be casted to String[], to use each element as a String[]?
EDIT:
Here is my code:
Iterator itr = getIdAndName().iterator();
while( itr.hasNext() ) {
String[] stringArray = (String[])itr.next();
String id = stringArray[0];
String name = stringArray[1];
System.out.println(id + ": " + name);
}
getIdAndName() returns Vector<String[]>.
It isn't. The only thing I can think of is you're not typing your iterator, i.e. you're doing this:
Vector<String[]> vector;
Iterator it = vector.iterator();
Object obj = it.next();
when you should be doing:
Vector<String[]> vector;
Iterator<String[]> it = vector.iterator();
String[] next = it.next();
Well, in most cases you don't actually need the iterator directly, so you could just use:
Vector<String[]> vector;
for (String[] element : vector) {
//...
}
The iterator() method returns an Iterator<String[]> when called on a variable whose declared type is Vector<String[]>.
I suspect that you are calling it on a variable that is declared as Vector or Vector<?> or something else. Or maybe you are assigning the iterator to an Iterator or Iterator<?> variable instead of an Iterator<String[]>. Obviously, this is just conjecture, because you didn't show us the source code.
(Note that it is the declared type of the variable that determines whether a cast is required ... not the actual type of the instance.)
When you get iterator from your vector, example vector.iterator(); your iterator should have parameterized. This way, the iterator will know the object that it stored is of type String[], if you do not tell iterator of the type it stored, it will have to resort to the object.
When you properly parameterized the type, for example Iterator<String[]> iter = vector.iterator(); , and then the iteration over iter will not need to be cast explicitly.
Please refer here about generic.
When you iterate over a Vector, you are using an implementation of the Iterator interface. By DEFINITION, the Iterator interface returns an Object when its next() method is called. You can see this definition here -
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Iterator.html
So it doesn't matter what you iterate over, the Iterator will always return an Object when you call its next() method.
And by the way, .next() does not HAVE to be casted to a String[]. It is just the case that in your specific case the Vector contains String[] as its elements, and therefore they are casted to String[] in order to use them.