Why does the same Map.Entry change when calling iterator.remove() in Java TreeMap?
Asked Answered
C

1

21

When I use Iterator to iterate some TreeMap, I found the same Map.Entry's content will change. For example:

import java.util.Map.Entry;
import java.util.TreeMap;

public class Solution {
    public static void main(String[] args) {
        TreeMap<Integer, Integer> map = new TreeMap<>();
        map.put(1,1);
        map.put(2,2);
        map.put(3,3);
        System.out.println("map: " + map);
        Map<Integer, Integer> fromMap = map.tailMap(2);
        System.out.println("fromMap: " + fromMap);
        Iterator<Entry<Integer, Integer>> iter = fromMap.entrySet().iterator();
        Entry<Integer, Integer> entry = iter.next();
        System.out.println(entry); // line 1  
        iter.remove();
        System.out.println(entry); // line 2. Why does entry content change?
    }
}

result:

map: {1=1, 2=2, 3=3}
fromMap: {2=2, 3=3}
2=2
3=3

The entry in line 1 and line 2 of the above code has the same reference, however the content changes when I call iter.remove().

Cornelison answered 25/6, 2021 at 17:41 Comment(2)
Looks like an inconvenient side effect of the implementation of the remove method of the iterator of the tail map view, even though it does not violate the contract. In all cases, you probably should not store references to an Entry, rather you should copy its content to some holder class and work on it.Kiangsi
The best way to copy a Map.Entry is to use Map.Entry.copyOf(), new in JDK 17.Vicarage
B
25

To be clear from Javadoc Map.Entry

The behavior of a map entry is undefined if the backing map has been modified after the entry was returned by the iterator, except through the setValue operation on the map entry

And from Map.Entry.getValue()

Returns the value corresponding to this entry. If the mapping has been removed from the backing map (by the iterator's remove operation), the results of this call are undefined

That means Java doesn't give guarantee that what happens if you call entry after remove method and it's undefined.

Boiler answered 25/6, 2021 at 18:5 Comment(3)
In other words, the implementation does not break the contract. But the behavior is really dangerous. I'd have hoped to get immutable entries!Beatabeaten
I never thought the same Entry can change before. Thank you so much for your explanantion!Cornelison
For reference, here's a comment of Joshua Bloch on Twitter on the reason why this choice had been made back in the days : It seemed like a good idea at the time. Computers were 1000 times slower and had 1000 times less memory (roughly speaking).Quartus

© 2022 - 2024 — McMap. All rights reserved.