2

While working on a problem statement, I need to access the next Entry of a Map and based on a condition, I need to modify the next entry value.

I have been considering the solution provided here: How to get the previous key/value and the next key/value in Maps where I can access the higherEntry using NavigableMap but it throws:

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractMap$SimpleImmutableEntry.setValue(AbstractMap.java:797)

which is totally understood. Here is the code snippet

for (Map.Entry<Character, Integer> current : myMap.entrySet()) {

    Map.Entry<Character, Integer> next = myMap.higherEntry(current.getKey());

    if (current.getValue() == next.getValue()) {
        // Code block
    } else if (current.getValue() > next.getValue()) {
        // Code block
    } else {
        next.setValue(1); // Line that throws Exception
    }
}

How can I Iterate through a Map, access and modify the next Entry when required?

ernest_k
  • 44,416
  • 5
  • 53
  • 99
Monil Panchal
  • 328
  • 2
  • 12
  • 2
    Why not just loop through the list of keys, then use the keys to retrieve the values for comparison, then use `Map.put()` to overwrite it? The implementation of the `Map` you are using uses the `SimpleImmutableEntry` implementation for the entries, which doesn't allow you to use `setValue()`. – Jai Feb 14 '19 at 05:39

4 Answers4

0

Instead of using an enhanced for loop, you can use a normal for loop and keep track of the index. This is tricky because you need a way to access the next entry and it depends on if you are using a ordered implementation of Map (like LinkedHashMap). If you are not using LinkedHashMap or some similar ordered map this will not be consistent and the entries could get out of order. Assuming you are using an ordered map, you can first convert the entry set to an array and go from there. This would look like Map.Entry<Character, Integer>[] entries = myMap.entrySet().stream().toArray(Map.Entry[]::new); Then, you can use a for loop along with this array.

for (int i = 0; i < entries.length-1; i++) {
    Map.Entry<Character, Integer> current = entries[i];
    Map.Entry<Character, Integer> next = entries[i+1];
    //code that does stuff
}
SizableShrimp
  • 666
  • 7
  • 12
0

TreeMap.higherEntry returns an immutable entry, and that explains the UnsupportedOperationException that you're seeing:

//exportEntry is being called by TreeMap.higherEntry(K)
static <K,V> Map.Entry<K,V> exportEntry(TreeMap.Entry<K,V> e) {
    return (e == null) ? null :
        new AbstractMap.SimpleImmutableEntry<>(e);
}

One possible solution/workaround is to collect your updated entries into a map and then modify your original map after the loop:

Map<Character, Integer> updates = new HashMap<>();

for (Map.Entry<Character, Integer> current : myMap.entrySet()) {

    Map.Entry<Character, Integer> next = myMap.higherEntry(current.getKey());

    if(null != next) {
        if (current.getValue() == next.getValue()) {
        } else if (current.getValue() > next.getValue()) {
        } else {
            updates.put(next.getKey(), 1); // entry to be updated
        }
    }
}

myMap.putAll(updates);

Note that I've added a null check as myMap.higherEntry may return null.

ernest_k
  • 44,416
  • 5
  • 53
  • 99
0
        Map.Entry<String, Integer> next = myMap.higherEntry(current.getKey());

        if(next != null)
        {
            if (current.getValue() == next.getValue())
            {
                //Code block
            }
            else if (current.getValue() > next.getValue()) {
                //Code block
            }

            else {
                myMap.put(current.getKey(), 1); 

            }
        }
0

The method higherEntry returns an immutable entry, but you code also lacks the handling of the case that there is no higher entry. On the other hand, the next entry returned by the iterator would be the right entry anyway (if there is one), so this can be done using an Iterator manually, like this:

Iterator<Map.Entry<Character, Integer>> it = myMap.entrySet().iterator();
if(it.hasNext()) {
    for(Map.Entry<Character, Integer> current=it.next(), next; it.hasNext(); current=next) {
        next = it.next();
        // don't compare Integer objects via ==, use equals
        if (current.getValue().equals(next.getValue())) {
            // Code block
        } else if (current.getValue() > next.getValue()) {
            // Code block
        } else {
            next.setValue(1);
        }
    }
}

A “not so clean” but reasonably simple alternative would be

Map.Entry<Character, Integer> current = null;
for(Map.Entry<Character, Integer> next: myMap.entrySet()) {
    if(current != null) {
        if (current.getValue().equals(next.getValue())) {
            // Code block
        } else if (current.getValue() > next.getValue()) {
            // Code block
        } else {
            next.setValue(1);
        }
    }
    current = next;
}
Holger
  • 285,553
  • 42
  • 434
  • 765