java - TreeMap put() 静默删除其他条目?

标签 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

...因此,从本质上讲,向 map 添加一个全新的键会导致不相关的条目从其中消失。

  • 如果我只单独添加 key1 和 keyN,key1 会保留在映射中——中间的 500 个条目在某种程度上很重要
  • 如果我从 2..(N-1) 中删除一个或两个任意键,添加 keyN 时 key1 仍会启动
  • 如果我从 2..(N-1) 中删除大范围的键,添加 keyN 时 key1 会保留,但在添加(比如)keyQ 时会消失,大约 300 个键
  • 不幸的是,当 keyN 踢出 key1 时的 map 大小与 keyQ 踢出 key1 时的 map 大小相同,所以这可能不是限制大小的问题
  • 如果我改用 HashMap,key1 会保留在映射中
  • 自定义键类 MyKey 对 Comparable、equals 和 hashCode 使用相同的逻辑。

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

最佳答案

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

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

来自TreeMap javadocs :

...map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal...

来自Comparable javadocs (感谢@Brian):

It is strongly recommended (though not required) that natural orderings be consistent with equals. This is so because sorted sets (and sorted maps) without explicit comparators behave "strangely" when they are used with elements (or keys) whose natural ordering is inconsistent with equals. In particular, such a sorted set (or sorted map) violates the general contract for set (or map), which is defined in terms of the equals method.

关于java - TreeMap put() 静默删除其他条目?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14631450/

相关文章:

java - 使用 JSObject 从 Java 调用 Javascript 函数

java - 以编程方式更改组件树上的布局/UI 元素顺序

ios - MKRouteStep:位置在哪里?

JAVA:字符串问题的 128 位 key 和返回

javascript - 将 child 分成 block 和 "react/no-array-index-key"

java - 为不同包中的任何类全局注册 XmlAdapter

java - 开始我的第一个 Android 应用程序。如何轻松测试服务器是否收到消息?

java - 根据其键的子集过滤 map 的元素,而无需遍历整个事物

javascript - 传单缩放和平移

java swing 键绑定(bind) - 缺少释放键的操作