Asked  7 Months ago    Answers:  5   Viewed   32 times

Note: I am aware of the Iterator#remove() method.

In the following code sample, I don't understand why the List.remove in main method throws ConcurrentModificationException, but not in the remove method.

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer toRemove) {
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer toRemove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }
}

 Answers

33

Here's why: As it is says in the Javadoc:

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException.

This check is done in the next() method of the iterator (as you can see by the stacktrace). But we will reach the next() method only if hasNext() delivered true, which is what is called by the for each to check if the boundary is met. In your remove method, when hasNext() checks if it needs to return another element, it will see that it returned two elements, and now after one element was removed the list only contains two elements. So all is peachy and we are done with iterating. The check for concurrent modifications does not occur, as this is done in the next() method which is never called.

Next we get to the second loop. After we remove the second number the hasNext method will check again if can return more values. It has returned two values already, but the list now only contains one. But the code here is:

public boolean hasNext() {
        return cursor != size();
}

1 != 2, so we continue to the next() method, which now realizes that someone has been messing with the list and fires the exception.

Hope that clears your question up.

Summary

List.remove() will not throw ConcurrentModificationException when it removes the second last element from the list.

Tuesday, June 1, 2021
 
Anax
answered 7 Months ago
38

I believe this is the purpose behind the Iterator.remove() method, to be able to remove an element from the collection while iterating.

For example:

Iterator<String> iter = li.iterator();
while(iter.hasNext()){
    if(iter.next().equalsIgnoreCase("str3"))
        iter.remove();
}
Friday, June 4, 2021
 
leebriggs
answered 7 Months ago
73

It's hard to say it's a bug, because gcc mixes up code for optimization and for creating warnings, which is even documented for this particular warning:

-Wuninitialized
Warn if an automatic variable is used without first being initialized or if a variable may be clobbered by a setjmp call. [...]
Because these warnings depend on optimization, the exact variables or elements for which there are warnings depends on the precise optimization options and version of GCC used.

(from GCC docs Options to Request or Suppress Warnings, emphasis mine)

You found an IMHO very silly case here. Try -O1 and you'll get an unexpected warning:

warn.c: In function ‘main’:
warn.c:13:6: warning: ‘i’ may be used uninitialized in this function [-Wmaybe-uninitialized]
     i++;
      ^

So, gcc still misses the first uninitialized use, but finds the second one! Try -O0 or -O2 and the warning is again gone...

You could still try to file a bug about this. Note clang gets it right:

warn.c:10:9: warning: variable 'i' is uninitialized when used here
      [-Wuninitialized]
  while(i!=len-1)
        ^
warn.c:6:8: note: initialize the variable 'i' to silence this warning
  int i,len=12;
       ^
        = 0
1 warning generated.
Wednesday, July 7, 2021
 
Ticksy
answered 6 Months ago
80

This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.

Below peice of code is causing the problem.

for(String s:hm.get(1)){
        hm.get(1).add("hello");
    }

You are iterating and modifying the same. Avoid this by creating new ArrayList

  ArrayList<String> ar1 = new ArrayList<String>();

for (String s : hm.get(1)) {
            ar1.add("hello");
        }

have a read here

Sunday, August 15, 2021
 
Naveed S
answered 4 Months ago
87

arr is clearly getting allocated on the stack, as you'd expect. From the pmap output, the stack is clearly growing to nearly 6GB:

00007ffe080a2000 5859388K rw---    [ stack ]

The question is therefore not about your program or the compiler. The question is why ulimit -s 8192 is apparently not being enforced.

From your answers to my various question, it is clear that somehow the ulimit -s setting is not being propagated from your shell through make run to your program. This to me seems very odd.

If I were in your shoes, I'd go through the system's limits.conf as well as the shared and the user's bash startup files to see if I can spot anything of relevance.

Saturday, October 30, 2021
 
Kyle Vassella
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share