展平多维 HashMap 以获得更好的性能?

Flatten multidimensional HashMaps for better performance?

我经常使用多维HashMaps,即包含HashMaps的HashMaps。例如,在双键的基础上,我 set/get 一个存储值

hashmapMulti.get(key1).put(key2,x);
hashmapMulti.get(key1).get(key2);

但是,我也可以使用 "flat" 散列图并将两个键组合成 set/get 一个值:

hashmapFlat.put(key1+"|"+key2,x);
hashmapFlat.get(key1+"|"+key2);

如果我没听错的话,put 和 get 的 时间复杂度 应该 "more or less" 是 O(1)对于哈希图。通过扁平化,我基本上用 3 个字符串组合的成本交换了 get(恒定时间)的成本。

哪种方式更快?

最佳选择是否取决于 HashMap 中存储的对象数量?

第三种选择是编写一个 class 来封装复合键。

它将有两个字段用于两个单独的键,如果您正确覆盖其 equals()hashCode() 方法,您将不必依赖字符串连接。

虽然在性能方面你最好的选择是编写实际的基准测试并比较你的实现,但这绝对是最干净的解决方案:它立即可读并且避免了对字符串连接的相当脆弱的依赖(即你可以有包含的键| 字符)。

get(key) 速度更快。

如您所知,从性能角度来看,使用字符串(尤其是串联)是 EVIL,因为最终,串联字符串是: - 创建一个新对象字符串 - 在第一个字符串上循环(成本:O(n)) - 在第二个字符串

上循环(成本:O(n))

(在您的示例中,您执行 2 次:1 次获取,1 次放置)

如果多维 hashmap 适合您的设计并正确表示您建模的内容,我认为使用它没有任何缺点。

如果你有大量的对象,二维 HashMap 的选择可能会增加你的内存占用空间,但由于我不知道你的约束(对象和可用内存的数量)我可以'说你是否需要去扁平化

Which way is faster?

您需要进行概要分析。我宁愿在默认情况下进行一次查找(使用 ,我有多个键)并且我只会在存在已证明的性能问题时才考虑其他方式。

Does the best choice depend in the number of objects stored in the HashMap(s)?

是的,但您可以对其进行管理,以便对于任意数量的对象,一个与两个一样好:

HashMap 有多个桶。来自键的哈希值,一个 32 位值被映射到一个更小的范围到 select 桶。这意味着具有不同哈希值的对象可以共享桶。当对象共享存储桶时,性能会随着存储桶的线性搜索而下降。

最坏的情况是散列函数 returns 一个常量导致所有键映射到一个桶,最好的情况是导致桶中键值对均匀分布。

可以增加桶的数量(HashMaps容量),结合好的哈希函数可以最大限度地减少桶的共享。

阅读本文并注意正确设置容量的建议:http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html

Which way is faster?

您需要对其进行基准测试...使用与您的实际应用程序将执行的操作密切相关的基准。您的实际应用 运行 真实数据将是理想的基准。

问题是这个问题变量太多,简单的分析是不合理的。考虑:

  • 如果使用两层嵌套map,那么每次查找都会涉及两组hash计算,数组探测,hash链查找

  • 但另一方面,使用组合键很可能需要在每次要进行查找时进行字符串连接。此外,如果我们假设用于查找的键字符串是临时的,则 String 类 hashcode 缓存将无效。

然后就是变量:

  • 查找与其他操作的比率,
  • 条目总数,
  • 组件密钥字符串的数量和平均长度,
  • 组件(或组合)键字符串被共享/重用的程度,
  • 应用程序其他部分的内存使用模式,等等。

最后是 apriori 二阶效应建模的困难,例如应用程序上下文中内存缓存、虚拟内存和垃圾收集器的性能影响。


我的建议是使用其中一种策略实施您的完整应用程序,然后对其进行基准测试(使用真实数据)并对其进行分析:

  • 如果基准测试和分析最终表明您的应用程序的这一部分对性能至关重要,则使用替代策略创建应用程序的第二个版本。

  • 最后对第二个版本进行基准测试和分析,然后决定哪个版本的性能最好。