【问题标题】:TreeMap put() silently deletes other entries?TreeMap put() 静默删除其他条目?
【发布时间】:2013-01-15 21:20:03
【问题描述】:

我遇到了一些非常令人毛骨悚然的 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) 中删除大范围的键,则在添加 keyN 时 key1 仍然存在,但在添加(例如)keyQ 时会丢失,大约 300 个键会进一步向下
  • 很遗憾,keyN 踢出 key1 时的地图大小与 keyQ 踢出 key1 时的地图大小相同,所以这可能不是限制大小的问题
  • 如果我改用 HashMap,key1 会保留在地图中
  • 自定义键类 MyKey 对 Comparable、equals 和 hashCode 使用相同的逻辑。

我最初使用 TreeMap 是因为我希望使用大型数据集,而 TreeMap 更节省内存。 HashMap 将是一个不错的选择,但看到 TreeMap 以这种方式运行仍然令人担忧——有人对这里发生的事情有想法吗?

【问题讨论】:

  • 同意史蒂夫郭的观点。注释掉您的 compareTo/equals/hashCode 实现并再次运行您的测试,看看您是否遇到同样的问题。

标签: java map key treemap comparable


【解决方案1】:

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

请注意,这种行为可能会因地图中元素的数量而异,因为它取决于实际比较两个元素,而比较方法的计算结果为 0。基本上,正如您所说,事情会表现得“诡异”。

来自TreeMap javadocs

...map 使用它的 compareTo(或比较)执行所有键比较 方法,因此该方法认为相等的两个键是,来自 排序图的立场,等于...

来自Comparable javadocs(感谢@Brian):

强烈建议(尽管不是必需的)自然 排序与equals一致。这是因为有序集 (和排序的地图)没有显式比较器的行为“奇怪”时 它们与自然顺序为的元素(或键)一起使用 与等号不一致。特别是,这样的排序集(或排序 map) 违反了 set(或 map)的一般约定,该约定被定义为 就equals方法而言。

【讨论】:

  • +1 这就是为什么 compareTo 一致性子句出现在 javadocs 中的原因:“强烈建议(尽管不是必需)自然排序与 equals 一致。这是因为排序集(和没有显式比较器的排序映射)在与自然顺序与等于不一致的元素(或键)一起使用时表现“奇怪”。 (Comparable)
  • 是的,就是这样——我缺少的是,踢出 key1 的实际 compareTo() 调用不一定发生在新键上,而是作为树重新平衡的一部分。谢谢!
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    您确定 keyN 在任何情况下都不会覆盖 key1 吗?因为在我看来那是你的幻象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-19
      • 2016-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多