【发布时间】:2017-08-24 13:53:10
【问题描述】:
我有一张地图。
Map<Integer,String> map = ...
地图有 n 个元素(我们以这 9 个元素为例)
map.put(1,"one");
map.put(2,"two");
map.put(3,"three");
map.put(4,"four");
map.put(5,"five");
map.put(6,"six");
map.put(7,"seven");
map.put(8,"eigth");
map.put(9,"nine");
现在我想迭代这张地图,并使用迭代器删除第 n 个元素。
private void remove(int num, final Map<Integer, String> map) {
Iterator<Map.Entry<Integer,String>> it = map.entrySet().iterator();
Map.Entry<Integer,String> entry;
while(it.hasNext()){
entry = it.next();
if(Integer.valueOf(num).equals(entry.getKey())){
it.remove();
System.out.println(entry.getValue());
// vs
// System.out.println(entry.getValue());
// it.remove();
}
}
}
从 javadoc 中,我假设删除的语义定义明确。
但是根据映射的实现——即 HashMap 与 TreeMap,it.remove() 是在 before 还是 after entry.getValue() 完成时有所不同。
对于 HashMaps map = new HashMap<>(),行为是
...
remove(4, map); //output: four
//or
remove(5, map); //output: five
对于 TreeMap map = new TreeMap<>(),行为是相同的,当我从迭代器中删除当前条目之后我访问了它:
System.out.println(entry.getValue());
it.remove();
结果
remove(4, map); //output: four
//or
remove(5, map); //output: five
到目前为止一切都很好,但是如果我删除元素 之前 我会访问该条目:
it.remove();
System.out.println(entry.getValue());
输出意外
remove(4, map); //output: five !!!
//or
remove(5, map); //output: five ok
显然,TreeMap 的it.remove() 修改了Entries,因为TreeMap 是由Entries 组成的,并且迭代器实际上返回了映射的实际元素。并且根据树中的当前位置,Entry 指向下一个或当前(已删除)元素的内部引用。
但我不确定这是错误还是故意的。如果是后者,不知道背后的原理是什么?
编辑: TreeMap iterator.remove()的源码
【问题讨论】:
-
我没有得到您在任一地图中描述的行为...您使用什么 Java 版本?如果在 remove() 之前和之后都输出条目的值会发生什么?
-
jdk1.8.0_121 ...它可以用只有 3 个条目 (1,2,3) 的 TreeMap 重现,删除中间元素 (2)。但它没有出现在 HashMap 中
-
在“二,三”前后打印出来
-
我现在也明白了。我无法想象这是故意的。执行此操作的代码有注释“如果严格内部,则将后继元素复制到 p,然后使 p 指向后继”......这并不是真正严格内部。
-
我们刚刚也遇到了这个问题。我在 Oracle 上为它做了一个错误报告——当它没有被审查时,我会为它添加一个链接。