我应该传递哪些值来为N个项目创建有效的HashMap
/ HashMap
结构?
在ArrayList
中,有效数字为N(N已假设未来增长)。 HashMap
的参数应该是多少? ((int)(N * 0.75d),0.75d)?更多?减?改变负载系数有什么影响?
答案 0 :(得分:36)
关于加载因子,我只是引用HashMap javadoc:
作为一般规则,默认负载系数(.75)在时间和空间成本之间提供了良好的权衡。较高的值会减少空间开销,但会增加查找成本(反映在HashMap类的大多数操作中,包括get和put)。在设置其初始容量时,应考虑映射中的预期条目数及其加载因子,以便最小化重新散列操作的数量。如果初始容量大于最大条目数除以负载因子,则不会发生任何重新连接操作。
意思是,不应该从.75
更改加载因子,除非您要进行某些特定的优化。初始容量是您想要更改的唯一内容,并根据您的N
值(意为(N / 0.75) + 1
)或该区域中的某些内容进行设置。这将确保表格总是足够大,不会发生重新散列。
答案 1 :(得分:18)
我运行了一些unit tests,看看这些答案是否正确,结果是使用:
(int) Math.ceil(requiredCapacity / loadFactor);
因为初始容量可以为HashMap
或Hashtable
提供所需的内容。通过“你想要的”我的意思是向地图添加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的幂的问题。
答案 2 :(得分:15)
在Google的guava libraries中,有一个函数可以创建针对预期数量的项目优化的HashMap:newHashMapWithExpectedSize
来自文档:
创建一个HashMap实例,具有足够高的“初始容量”,它应该保留expectedSize元素而不会增长......
答案 3 :(得分:5)
值得注意的是,在较小的一侧使用HashMap会使哈希冲突更有可能,这可能会减慢查找速度。因此,如果你真的担心地图的速度,而不是它的大小,那么它可能值得使它需要保持的数据有点太大。由于内存很便宜,我通常会使用
为已知数量的项目初始化HashMapsHashMap<Foo> myMap = new HashMap<Foo>(numberOfElements * 2);
随意不同意,事实上我非常希望验证或抛弃这个想法。
答案 4 :(得分:3)
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中的元素数。这将确保最快的获取/放置速度。
答案 5 :(得分:1)
呃,不,不,除非我误解你在这里说的话。将整数传递给Arraylist构造函数时,它将创建一个完全相同大小的底层数组。如果事实证明你甚至需要一个额外的元素,那么当你下次调用add()时,ArrayList将需要调整底层数组的大小,导致这个调用比通常花费更长的时间。在ArrayList中,有效数字为N(N已假设未来增长)。
另一方面,如果你在谈论你考虑增长的N的价值 - 那么是的,如果你能保证价值永远不会超过这个,那么调用这样的Arraylist构造函数是合适的。在这种情况下,正如Hank所指出的,地图的类似构造函数将是N和1.0f。即使您确实碰巧超过N,这应该合理地执行(尽管如果您希望定期发生这种情况,您可能希望为初始大小传递更大的数字)。
如果您不知道,负载系数是地图容量增加的点,占总容量的一小部分。
编辑:Yuval可能是正确的,将通用映射的负载因子保持在0.75附近更好。如果您的密钥具有顺序哈希码(例如顺序整数键),则加载因子1.0将表现出色,但对于其他任何事情,您可能会遇到与哈希桶的冲突,这意味着某些元素的查找需要更长时间。创建比严格必要的更多桶将减少碰撞的机会,这意味着元素更有可能存在于自己的桶中,因此可以在最短的时间内检索。正如文档所说,这是时间与空间的权衡。如果其中任何一个对您特别重要(如分析器所示,而不是过早优化!)您可以强调这一点;否则,坚持默认。
答案 6 :(得分:1)
参考HashMap源代码会有所帮助。
如果条目数达到阈值(容量*负载系数),则自动进行重新散列。这意味着当条目增长时,过小的负载因子会导致频繁的重复。
答案 7 :(得分:1)
在List
和Map
初始化的大多数情况下,使List
或Map
具有以下大小参数是安全的。
List<T>(numElements + (numElements / 2));
Map<T,T>(numElements + (numElements / 2));
这符合 .75 规则,并且比上述 * 2
操作节省了一些开销。
答案 8 :(得分:0)
对于关键系统中的非常大的HashMaps,初始容量错误可能非常有问题,您可能需要经验信息来确定如何最好地初始化Map。
CollectionSpy(collectionspy.com)是一个新的Java分析器,它可以让你在眨眼之间看到HashMaps接近需要重新散列,过去多少次重新散列,以及更多。一种理想的工具,用于确定基于容量的容器构造函数的安全初始容量参数。