简体   繁体   中英

How I can get ConcurrentModificationException while iterating Hashmap?

I am trying to add a key value pair to the hashmap inside the Iterator method.

But this is not giving me ConcurrentModificationException . Why?

Since Hashmap is failfast.

Map<String,String> m = new HashMap<>();
           m.put("a", "a");

           Iterator<String> i = m.keySet().iterator();
           while(i.hasNext()){
               System.out.println(i.next());
               m.put("dsad", "asfsdf");

           }

If this is wrong, How i can produce ConcurrentModificationException ? Thanks.

Update: Just checked.

Map<String,String> m = new HashMap<>();
               m.put("a", "a");
          m.put("abc", "a");

               Iterator<String> i = m.keySet().iterator();
               while(i.hasNext()){
                   System.out.println(i.next());
                   m.put("dsad", "asfsdf");

               }

This is giving me the exception.

It happens that the concurrent modification check done by the HashMap code fails to detect this situation. The code for HashMap 's iterator's hasNext in Oracle's JDK7 is:

public final boolean hasNext() {
    return next != null;
}

...where (confusingly!) that next is a private data member in the iterator class (not to be confused with the next method on the Iterator interface — to my mind, calling that data member next was a very poor choice).

Note that it doesn't do the check for concurrent modifications. Contrast with this code that is called (indirectly) from Iterator#next :

    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();

...which does do the check.

So here's what happens in your code:

  1. You create a HashMap .
  2. You add one entry to it.
  3. You begin an iteration.
  4. hasNext is true, so you go into the body of your loop.
  5. You get the element from next ; at this point, the iterator remembers what the next element should be on its internal data member (the confusingly-named next ), and in this case since there's no next element in the map, that next data member is set to null , meaning that the iteration is complete.
  6. You add to the map.
  7. Your code calls hasNext , which sees the next data member is null and returns false .

If you had two elements in the map before starting your loop instead of one, you'd get the exception (from next ).

I've previously argued this is, or is very nearly, a bug, but it's a pretty fuzzy area, and others have argued quite reasonably that it isn't. The documentation doesn't say specifically which methods of Iterator<E> will throw the exception, just that it will get thrown. The documentation also says it's only thrown on a "best effort" basis, it's not guaranteed.

Whether one considers this a bug or not, it's unlikely to be changed at this point, as the pain of changing it (breaking some existing code that probably shouldn't have relied on this behavior) far outweighs the benefit (possibly being more "correct").

The iterator may throw ConcurrentModificationException but is not guaranteed to.

From the javadoc of HashMap:

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

Try this:

  Map<String,String> m = new HashMap<>();
    m.put("a", "a");

    Iterator<String> i = m.keySet().iterator();
    while(i.hasNext()){
        m.remove("a");
        System.out.println(i.next());


    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM