我有一张地图。
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 vs TreeMap,{/ 1>}在之前完成或之后是 {{1} }。
对于HashMaps it.remove()
,行为是
entry.getValue()
对于TreeMap map = new HashMap<>()
行为是相同的,当我在访问它之后从迭代器中删除当前条目时:
...
remove(4, map); //output: four
//or
remove(5, map); //output: five
结果
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());
的{{1}}会修改remove(4, map); //output: five !!!
//or
remove(5, map); //output: five ok
,因为it.remove()
由TreeMap
组成,而迭代器实际上返回了实际的元素的地图。并且根据树中的当前位置,Entry的内部引用指向下一个或当前(已删除)元素。
但我不确定这是否是一个错误,或者这是否是故意的。如果是后者,我想知道背后的理由吗?
编辑: 源代码TreeMap iterator.remove()
答案 0 :(得分:2)
如果在迭代器返回条目后修改了支持映射,则映射条目的行为是未定义的,除非通过映射条目上的setValue操作
如果映射已从支持映射中删除(通过迭代器的删除操作),则此调用的结果未定义。
禁止在entry.getValue()
之后致电it.remove()
。如果您尝试将会发生什么,Java不做任何承诺。您应该在删除条目之前检索该值。
答案 1 :(得分:0)
这是一个部分答案,但希望能让其他人知道如何提供更准确的答案。
假设您对Iterator#next()
的通话会返回位于基础地图顶部的视图,
然后,当从地图中删除基础地图条目后,尝试访问值时,我们可能会得到一些未定义的行为,这可能不足为奇。
while (it.hasNext()) {
entry = it.next();
if (Integer.valueOf(num).equals(entry.getKey())) {
it.remove();
// the object which 'entry' points to is already removed
// what is this pointing to?
System.out.println(entry.getValue());
}
}
根据您自己的观察,行为似乎是Map
具体实施,暗示 执行特定地图涉及哪种行为