ImmutableMap 是大量 keys/objects/ 的次优选择吗?

Is ImmutableMap a sub-optimal choice for large volume of keys/objects/

我和同事一起做一些测试,我们从数据库中提取数据(大约 350,000 条记录),将每条记录转换为一个对象和一个关键对象,然后将它们填充到一个 ImmutableMap.Builder.

当我们调用 build() 方法时,它花了很长时间,可能是由于 ImmutableMap 附带的所有数据完整性检查(重复键、空值等)。公平地说,我们也尝试使用哈希图,这花了一些时间,但没有 ImmutableMap 长。我们最终决定只使用 ConcurrentHashMap,我们在迭代记录时填充了 9 个线程,并将其包装在一个不可修改的映射中。表现很好。

我在其阅读的文档中注意到 ImutableMap 未针对 "equals()" 操作进行优化。作为一个顽固的不可变主义者,我希望 ImmutableMap 能够处理大量数据,但我觉得它并不适用于此。这个假设对吗?是否仅针对 small/medium-sized 数据集进行了优化?是否有我需要通过 "copyOf()" 或其他方式调用的隐藏实现?

我的经验是 Java 中的 none 内置 Collection 类 确实针对大容量性能进行了优化。例如,一旦 hashCode 被用作数组中的索引,HashMap 就使用简单的迭代,并通过 equals 将键与具有相同散列的每个项目进行比较。如果您要在地图中存储数百万个项目,那么您需要设计良好的散列和大容量。这些 类 旨在尽可能通用和安全。

因此,如果您希望坚持使用标准 Java HashMap:

,请尝试性能优化
  1. 确保您的散列函数提供尽可能接近均匀的分布。许多域的值都存在偏差,您的哈希值需要考虑到这一点。
  2. 当你有很多数据时HashMap会扩展很多倍。理想情况下将初始容量设置为尽可能接近最终值。
  3. 确保您的 equals 实施尽可能高效。

如果你知道(例如)你的密钥是一个整数,你可以应用大量的性能优化,例如在应用哈希后使用某种形式的 btree 并使用 == 而不是 equals.

所以简单的答案是我相信您需要编写自己的集合来获得您想要的性能或使用可用的更优化的实现之一。

我猜你的key.equals()是一个耗时的方法。

key.equals()ImmutableMap.build() 中被调用的次数 HashMap.put() 多得多 (在循环中)。 key.hashCode() 被调用 HashMap.put()ImmutableMap.build()。因此,如果key.equals()花费的时间较长,则整个持续时间可能会相差很多。

key.equals()HashMap.put() 期间被调用了几次(良好的哈希算法会导致一些冲突)。 而在 ImmutableMap.build() 的情况下, key.equals() 将在 checkNoConflictInBucket() 时被调用多次。 key.equals() 的 O(n)。

一旦构建好地图,两种类型的地图在访问时应该不会有太大差异,因为它们都是基于哈希的。

样本: 有 10000 个随机字符串作为键。 HashMap.put() 调用
String.equals() 2 次,而 ImmutableMap.build() 调用 3000 次。