假设我需要在Hashset中存储1000个对象,我是否有1000个桶包含每个对象(通过为每个对象生成哈希码的唯一值)或者有10个桶大致包含100个对象?
拥有唯一存储桶的一个优点是我可以在调用equals()方法时节省执行周期吗?
为什么设置一定数量的存储桶并尽可能均匀地分配对象是非常重要的?
理想的铲斗比率应该是多少?
答案 0 :(得分:8)
为什么设置一定数量的存储桶并尽可能均匀地分配对象是非常重要的?
HashSet
应该能够平均确定O(1)时间的成员资格。来自documentation:
这个类为基本操作(添加,删除,包含和大小)提供恒定的时间性能,假设散列函数在桶之间正确地分散元素。
a Hashset
用于实现此目的的算法是检索对象的哈希码,并使用它来查找正确的桶。然后它迭代桶中的所有项目,直到找到相同的项目。如果存储桶中的项目数大于O(1),则查找将花费超过O(1)时间。
在最坏的情况下 - 如果所有项目都散列到同一个存储桶 - 将需要O(n)时间来确定对象是否在集合中。
理想的铲斗比率应该是多少?
这里有一个时空权衡。增加桶的数量会减少碰撞的可能性。但是,它也增加了内存需求。哈希集有两个参数initialCapacity
和loadFactor
,允许您调整HashSet
应创建的桶数。默认加载因子为0.75,这对于大多数用途都很好,但如果您有特殊要求,则可以选择其他值。
有关这些参数的更多信息,请参阅HashMap
的文档:
此实现为基本操作(get和put)提供了恒定时间性能,假设散列函数在桶之间正确地分散元素。对集合视图的迭代需要与HashMap实例的“容量”(桶的数量)加上其大小(键 - 值映射的数量)成比例的时间。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或负载因子太低)非常重要。
HashMap的一个实例有两个影响其性能的参数:初始容量和负载因子。容量是哈希表中的桶数,初始容量只是创建哈希表时的容量。加载因子是在自动增加容量之前允许哈希表获取的完整程度的度量。当哈希表中的条目数超过加载因子和当前容量的乘积时,通过调用rehash方法,容量大致加倍。
作为一般规则,默认负载系数(.75)在时间和空间成本之间提供了良好的权衡。较高的值会减少空间开销,但会增加查找成本(反映在HashMap类的大多数操作中,包括get和put)。在设置其初始容量时,应考虑映射中的预期条目数及其加载因子,以便最小化重新散列操作的数量。如果初始容量大于最大条目数除以加载因子,则不会发生重新加载操作。
答案 1 :(得分:1)
每个元素大约一个桶对处理器来说更好,太多桶对内存不利。 Java将从少量存储桶开始,并在开始填充后自动增加HashSet的容量,因此除非您的应用程序出现性能问题并且您已将哈希集确定为原因,否则您不需要关心。 / p>
如果每个存储桶中有多个元素,查找开始需要更长时间。如果你有很多空桶,你使用的内存比你需要的多,并且迭代元素需要更长的时间。
这似乎是过早的优化等待发生 - 在大多数情况下默认构造函数都没问题。
答案 2 :(得分:1)
Object.hashCode()
属于int
类型,您只能拥有 2 ^ 32 不同的值,这就是您创建存储桶并在其中分配对象的原因。
编辑:如果您使用2^32
存储桶存储2 ^ 32对象,则默认获取操作将为您提供持续的复杂性,但是当您逐个插入元素以存储{{然后,如果我们使用2^32
作为存储区,那么对象然后重新执行将会执行,然后每当它超过Object[]
的长度时,它将创建具有更大尺寸的新数组,并且将元素复制到此中。这个过程会增加复杂性。这就是为什么我们使用array
和equals
的比例,hashcode
本身通过提供更好的Hashsets
来完成。