TreeMap put()会默默删除其他条目吗?

时间:2013-01-31 17:17:03

标签: java map key treemap comparable

我遇到了一些非常怪异的TreeMap行为,我在缩小测试用例方面遇到了一些麻烦,所以请耐心等待。

我想从运行时提供的文件中将大量键值对读入Map。我正在使用自定义密钥类。后来,当我把条目撤回时,我发现其中一个或多个丢失了。使用调试器和一些测试用例,我已经确定缺少的条目在读取阶段肯定会消失,但我不确定是什么导致它。

基本上:

Map<MyKey,Double> map = new TreeMap<MyKey,Double>();
map.put(key1,value1);

// ... put another ~500 entries into the map ...

assertTrue(map.containsKey(key1)); // passes
if (!map.containsKey(keyN)) { 
    map.put(keyN, valueN); // this code executes
}
assertTrue(map.containsKey(key1)); // FAILS

...基本上,向地图添加一个全新的密钥会导致不相关的条目失效。

  • 如果我只是单独添加key1和keyN,则key1会保留在地图中 - 介入的500个条目在某种程度上非常重要
  • 如果我从2 ..(N-1)中删除一个或两个任意键,则在添加keyN时仍然会启动key1
  • 如果我从2 ..(N-1)中移除了大范围的键,则key1在添加keyN时保持不变,但在添加keyQ时会掉线,在行下面会有~300个键
  • 不幸的是,当keyN踢出key1时,当keyN踢出key1时,地图的大小与地图的大小相同,所以它可能不是一个有限大小的问题
  • 如果我改为使用HashMap,则key1会保留在地图中
  • 自定义键类MyKey对Comparable,equals和hashCode使用相同的逻辑。

我最初使用的是TreeMap,因为我希望使用大型数据集,而TreeMap的内存效率要高一些。 HashMap将是一个很好的选择,但看到TreeMap以这种方式运行仍然令人担忧 - 任何人都对这里发生的事情有所了解?

3 个答案:

答案 0 :(得分:12)

TreeMap认为两个条目相等,如果比较时,结果为0.因此,如果key1和keyN'与'0'比较,则key1将被putN()覆盖。这是真的即使 !key1.equals(keyN)。因此,虽然您可能认为这两个键不相等,因此插入一个不应该覆盖另一个,如果您的equals和比较函数彼此不一致,TreeMap会认为不同。

请注意,此行为可能会因地图中元素的数量而有所不同,因为它取决于实际比较两个比较方法评估为0的元素。基本上,事情会像你说的那样“诡异”。

来自TreeMap javadocs

  

... map使用compareTo(或比较)执行所有关键比较   方法,所以这两种方法被认为是相同的,来自   有序地图的立场,等于......

来自Comparable javadocs(感谢@Brian):

  

强烈建议(尽管不要求)自然   排序与平等一致。这是因为排序集   没有显式比较器的(和排序的地图)表现得“奇怪”   它们与自然顺序的元素(或键)一起使用   与equals不一致。特别是,这样的排序集(或排序的   map)违反了定义的集合(或映射)的一般合同   就平等方法而言。

答案 1 :(得分:1)

向我们展示MyKey的代码。我的猜测是你的compareTo方法有问题。更具体地说,您的compareToequals不一致。

答案 2 :(得分:0)

您确定keyN在任何情况下都没有覆盖key1吗?因为在我看来那是你的幻影案例。