user704010
user704010

Reputation:

Replace/Changing HashMap items during iteration

I get Concurrent Modification Exception when i try to remove an item from HashMap. I know that removing items during iteration through HashMap will trigger this exception , but i need to replace older item with new one. How can i do this ? Maybe to create a copy of countNumberOfEachCharacter HashMap , and whe iterate through original HashMap to remove item from copy HashMap ?

countNumberOfEachCharacter = new HashMap<Character,Character>();
if (countNumberOfEachCharacter.containsKey(word.charAt(i))) {
    System.out.println("This character already exists");                     
    for (Iterator it = countNumberOfEachCharacter.entrySet().iterator(); it.hasNext();) {

      Map.Entry entry = (Map.Entry) it.next();

      Object key = entry.getKey();
      Object value = entry.getValue();

      if (key.equals(word.charAt(i))) { 

        int toIncrease = Integer.parseInt(value.toString());
        toIncrease++;

        System.out.println("key  "+key);                                                     
        System.out.println("increased  "+toIncrease);                                                       
        countNumberOfEachCharacter.remove(word.charAt(i));

        char c = Character.forDigit(toIncrease, 10);                                                     
        countNumberOfEachCharacter.put(word.charAt(i),c);                                                                                                                                                                               
    }                                                                                                                                                                                                           
  }                                                                           
}
else {    

   System.out.println("First time found this character");

   char c = Character.forDigit(1, 10);                                
   countNumberOfEachCharacter.put(word.charAt(i),c);                            
   System.out.println("Stored "+word.charAt(i)+" with count "+c);                                                               
}

Upvotes: 2

Views: 10205

Answers (3)

Peter Lawrey
Peter Lawrey

Reputation: 533500

The whole point of a map is that you can lookup by key, you don't have to examine every entry.

Map<Character, AtomicInteger> countNumberOfEachCharacter = new TreeMap<Character, AtomicInteger>();

String word = "the quick brown fox jumps over the lazy dog";
for (int i = 0; i < word.length(); i++) {
    AtomicInteger count = countNumberOfEachCharacter.get(word.charAt(i));
    if (count == null)
        countNumberOfEachCharacter.put(word.charAt(i), new AtomicInteger(1));
    else
        count.incrementAndGet();
}
System.out.println("Character count: " + countNumberOfEachCharacter);

prints

Character count: { =8, a=1, b=1, c=1, d=1, e=3, f=1, g=1, h=2, i=1, j=1, k=1, l=1, m=1, n=1, o=4, p=1, q=1, r=2, s=1, t=2, u=2, v=1, w=1, x=1, y=1, z=1}

However since you have a small, fixed number of possible characters you don't even need to use a Map

int[] countNumberOfEachCharacter = new int[Character.MAX_VALUE + 1];

String word = "the quick brown fox jumps over the lazy dog";
for (int i = 0; i < word.length(); i++)
    countNumberOfEachCharacter[word.charAt(i)]++;

System.out.print("Character count: ");
for (int i = 0; i < countNumberOfEachCharacter.length; i++)
    if (countNumberOfEachCharacter[i] > 0)
        System.out.print(" " + (char) i + "=" + countNumberOfEachCharacter[i]);
System.out.println();

prints

Character count:   =8 a=1 b=1 c=1 d=1 e=3 f=1 g=1 h=2 i=1 j=1 k=1 l=1 m=1 n=1 o=4 p=1 q=1 r=2 s=1 t=2 u=2 v=1 w=1 x=1 y=1 z=1

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074276

...but i need to replace older item with new one

I take it from "replace" (and from the code you've quoted) that the key remains the same, it's just the value that differs. If so, I don't believe calling setValue on Map.Entry objects causes a ConcurrentModificationException, so you could do that.

Update: Just tested it, and indeed, it works:

import java.util.*;

public class ReplaceMapEntryValue {

    public static final void main(String[] args) {
        Map m;
        Iterator<Map.Entry> it;
        Map.Entry entry;

        // Create
        m = new HashMap();
        m.put("a", "alpha");
        m.put("b", "beta");

        // Update
        it = m.entrySet().iterator();
        while (it.hasNext()) {
            entry = it.next();
            if (entry.getKey() == "b") {
                entry.setValue("bravo");
            }
        }

        // Show
        it = m.entrySet().iterator();
        while (it.hasNext()) {
            entry = it.next();
            System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());
        }

        // Done
        System.exit(0);
    }
}

Upvotes: 1

Robin
Robin

Reputation: 36611

While iterating over a Collection, you can only remove elements by using the Iterator#remove method. This is also documented in the class javadoc of HashMap

The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future

Furthermore for what you are trying to do (=update a value) you do not have to remove it. Just call put with that key and an updated value, which will update the value, as documented in the javadoc of the HashMap#put method

Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced.

Upvotes: 2

Related Questions