我应该传递哪些值来为N个项目创建高效的HashMap / ArrayList基础结构?

在<=>中,有效数字是N(N已经假设未来增长)。 <=>的参数应该是多少? ((int)(N * 0.75d),0.75d)?更多?减?改变负载系数有什么影响?

有帮助吗?

解决方案

关于加载因子,我只是引用 HashMap javadoc

  

作为一般规则,默认负载系数(.75)在时间和空间成本之间提供了良好的权衡。较高的值会减少空间开销,但会增加查找成本(反映在HashMap类的大多数操作中,包括get和put)。在设置其初始容量时,应考虑映射中的预期条目数及其加载因子,以便最小化重新散列操作的数量。如果初始容量大于最大条目数除以负载因子,则不会发生任何重新连接操作。

意思是,不应该从.75更改加载因子,除非您要进行某些特定的优化。初始容量是您想要更改的唯一内容,并根据您的N值 - 意味着(N / 0.75) + 1或该区域中的某些内容进行设置。这将确保表格总是足够大,不会发生重新散列。

其他提示

我运行了一些单元测试看看这些答案是否正确,结果是:

(int) Math.ceil(requiredCapacity / loadFactor);

因为初始容量可以为HashMapHashtable提供所需的内容。通过<!>“你想要什么<!>”;我的意思是将requiredCapacity元素添加到地图中不会导致它包装的数组调整大小,并且数组不会大于所需数组。由于默认加载容量为0.75,因此初始化HashMap可以起作用:

... = new HashMap<KeyType, ValueType>((int) Math.ceil(requiredCapacity / 0.75));

由于HashSet实际上只是HashMap的包装器,因此同样的逻辑也适用于那里,即你可以像这样有效地构造一个HashSet:

.... = new HashSet<TypeToStore>((int) Math.ceil(requiredCapacity / 0.75));

@Yuval Adam的答案对于所有情况都是正确的,除非(requiredCapacity / 0.75)是2的幂,在这种情况下它分配了太多的内存。
@ NotEdible的答案在很多情况下使用了太多的内存,因为HashMap的构造函数本身处理了它希望maps数组的大小是2的幂的问题。

在Google的番石榴图书馆中,有一项功能创建一个针对预期数量的项目优化的HashMap: newHashMapWithExpectedSize

来自文档:

  

创建一个HashMap实例,其中包含足够高的<!>初始容量<!>;它应该保持expectedSize元素而不增长...

值得注意的是,在较小的一侧使用HashMap会使哈希冲突更有可能,这可能会减慢查找速度。因此,如果你真的担心地图的速度,而不是它的大小,那么它可能值得使它需要保持的数据有点太大。由于内存很便宜,我通常会使用

为已知数量的项目初始化HashMaps
HashMap<Foo> myMap = new HashMap<Foo>(numberOfElements * 2);

随意不同意,事实上我非常希望验证或抛弃这个想法。

Yuval给出的答案只适用于Hashtable。 HashMap使用两个幂的桶,因此对于HashMap,Zarkonnen实际上是正确的。您可以从源代码中验证这一点:

  // Find a power of 2 >= initialCapacity
  int capacity = 1;
  while (capacity < initialCapacity)
  capacity <<= 1;

因此,尽管Hashtable和HashMap之间的载荷因子0.75f仍然相同,但您应该使用初始容量n * 2,其中n是您计划存储在HashMap中的元素数。这将确保最快的获取/放置速度。

  

在ArrayList中,有效数字为N(N已假设未来增长)。

呃,不,不,除非我误解你在这里说的话。将整数传递给Arraylist构造函数时,它将创建一个完全相同大小的底层数组。如果事实证明你甚至需要一个额外的元素,那么当你下次调用add()时,ArrayList将需要调整底层数组的大小,导致这个调用比通常花费更长的时间。

另一方面,如果你在谈论你考虑增长的N的价值 - 那么是的,如果你能保证价值永远不会超过这个,那么调用这样的Arraylist构造函数是合适的。在这种情况下,正如Hank所指出的,地图的类似构造函数将是N和1.0f。即使您确实碰巧超过N,这应该合理地执行(尽管如果您希望定期发生这种情况,您可能希望为初始大小传递更大的数字)。

如果您不知道,负载系数是地图容量增加的点,占总容量的一小部分。

编辑:Yuval可能是正确的,将通用映射的负载因子保持在0.75附近更好。如果您的密钥具有顺序哈希码(例如顺序整数键),则加载因子1.0将表现出色,但对于其他任何事情,您可能会遇到与哈希桶的冲突,这意味着某些元素的查找需要更长时间。创建比严格必要的更多桶将减少碰撞的机会,这意味着元素更有可能存在于自己的桶中,因此可以在最短的时间内检索。正如文档所说,这是时间与空间的权衡。如果其中任何一个对您特别重要(如分析器所示,而不是过早优化!)您可以强调这一点;否则,坚持默认。

参考HashMap源代码会有所帮助。

如果条目数达到阈值(容量*负载系数),则自动进行重新散列。这意味着当条目增长时,过小的负载因子会导致频繁的重复。

ListMap初始化的大多数情况下,使* 2或<=>使用以下大小参数进行初始化是安全的。

List<T>(numElements + (numElements / 2));
Map<T,T>(numElements + (numElements / 2));

这遵循 .75 规则,并且比上述 <=> 操作节省了一点开销。

对于关键系统中非常大的HashMaps,初始容量错误可能会出现问题,您可能需要经验信息来确定如何最好地初始化Map。

CollectionSpy( collectionspy.com )是一款全新的Java分析器,可让您在眨眼间看到哪些HashMaps接近需要重新发布,过去有多少次重新发布,等等。一种理想的工具,用于确定基于容量的容器构造函数的安全初始容量参数。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top