TreeMap迭代器行为在删除时不一致

时间:2011-09-20 16:32:38

标签: java

今天在发现错误时,我发现TreeMap迭代器行为在删除对象时有点奇怪。事实上,我在一个简单的例子中测试了不同的用途:

    TreeMap<String, String> map = new TreeMap<String, String>();
    map.put("1", "1");
    map.put("2", "2");
    map.put("3", "3");
    map.put("4", "4");
    map.put("5", "5");

    Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();

     while (iterator.hasNext()) {
        Map.Entry<String, String> entry = iterator.next();
        System.out.println("Before "+entry.getKey());
        iterator.remove();
        System.out.println("After " +entry.getKey());
    }

结果是:

Before 1
After 1 
Before 2
After 2
Before 3
After 3
Before 4
After 4
Before 5
After 5

但如果我把它改为:

    TreeMap<String, String> map = new TreeMap<String, String>();
    map.put("1", "1");
    map.put("2", "2");
    map.put("3", "3");
    map.put("4", "4");
    map.put("5", "5");

    Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();

            String key = "4";

    while (iterator.hasNext()) {
        Map.Entry<String, String> entry = iterator.next();
        if(entry.getKey().equals(key)){
            iterator.remove();
            System.out.println(entry.getKey());
        }
    }

然后,对于key = 4,结果为5,对于key = 5,由于链接在删除时被更改,结果为5。但为什么这些行为会有所不同。 JIT?即使这是答案,它们不应该是统一的吗?

3 个答案:

答案 0 :(得分:6)

请参阅getValue()的{​​{3}}的Javadoc:

  

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

删除条目后,行为未定义,因此这可以解释您获得不同结果的原因。

注意:另一位用户aix发布了类似的答案,我将对此发表评论,但由于某种原因,它已被删除/删除。

编辑:我不确定这个答案是否正确,因为我刚注意到OP正在调用getKey()而不是getValue()

编辑2:感谢Ed Staub:我认为一般的想法仍然存在,因为Map.Entry状态的Javadoc:

  

...更正式地说,如果在迭代器返回条目后修改了支持映射,则映射条目的行为是未定义的,除非通过映射条目上的setValue操作。

答案 1 :(得分:2)

要回答关于为什么会发生这种情况的问题,评论会给出一个提示

// If strictly internal, copy successor's element to p and then make p
// point to successor.

AFAIK,这是保持树平衡的一部分。

这意味着如果您只有一个子节点(或没有)节点本身被删除。如果节点有两个子节点,则该节点将替换为其后继节点。

如果您始终从一开始就删除该节点,您仍然可以使用该节点,但不会对其进行修改。如果从树的中间删除右节点,则会修改该条目以平衡树,因此在删除后使用该条目可以看到修改后的条目。

答案 2 :(得分:0)

我个人不会称这是一种“奇怪”的行为。它可能不是你所期望的。

TreeMap在内部实现red-black tree。删除节点时,树得到了重新平衡(参见TreeMap.deleteEntry(Entry<K,V> p)),并且有几种情况如何完成这种重新绑定,这取决于当前树分别删除哪个节点。