为什么在 Java TreeMap 中调用 iterator.remove() 时相同的 Map.Entry 会发生变化?

map*_*ple 21 java collections treemap

当我使用Iterator迭代 some 时TreeMap,我发现相同Map.Entry的内容会发生变化。例如:

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?
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

map: {1=1, 2=2, 3=3}
fromMap: {2=2, 3=3}
2=2
3=3
Run Code Online (Sandbox Code Playgroud)

entry在第1行和上述代码中的线2具有相同的参考,但是当我所说的内容改变iter.remove()

Vin*_*gde 25

从 Javadoc Map.Entry 中明确

如果在迭代器返回条目后修改了后备映射,则映射条目的行为是未定义的,除非通过对映射条目的 setValue 操作

Map.Entry.getValue()

返回与此条目对应的值。如果映射已从支持映射中删除(通过迭代器的删除操作),则此调用的结果未定义

这意味着 Java 不保证如果您调用entryafterremove方法并且它未定义会发生什么 。

  • 也就是说,执行并不违反合同。但这种行为确实很危险。我本来希望获得不可变的条目! (2认同)
  • 作为参考,这里有 [Joshua Bloch 在 Twitter 上](https://twitter.com/joshbloch/status/1408718009833713669?s=21) 的评论,解释了当时做出这一选择的原因:“看起来就像当时是个好主意。计算机速度慢了 1000 倍,内存也少了 1000 倍(粗略地说)。 (2认同)