何时在Java HashMap中触发调整大小?

2020-05-06 22:54:28

HashMap是Java中最常用的集合类型之一,它存储键-值对。理想情况下,它期望使用哈希表,期望数据访问时间复杂度为O(1),然而,由于哈希冲突,现实中使用链表或红黑树来存储数据,这使得最坏情况下的时间复杂度为O(Logn)。

虽然集合使用像数组和链表这样的数据结构,但与数组不同的是,当没有足够的空间存储数据时,它们将动态调整大小,因为这涉及将数据从旧数组复制到新数组,这被认为是一项繁重的操作,因此通常可以指定初始容量,以避免在已经估计条目数量的情况下不必要的大小调整。

在决定正确的初始容量之前,需要回答的一个重要问题是何时在HashMap中触发调整大小?

HashMap提供了一个可以接受初始容量值的构造函数方法,最终该构造函数方法将调用另一个默认负载因子值为0.75的构造函数方法。

public HashMap(int initialCapacity,Float loadFactor){if(initialCapacity<;0)抛出新的IllegalArgumentException(";非法初始容量:";+initialCapacity);if(initialCapacity&>;Maximum_Capacity)initialCapacity=Maximum_Capacity;IF(loadFactor<;=0||Float.isNaN(Loadfactor))抛出新的IllegalArgumentException(&#。

这里的阈值是用于确定何时应该触发大小调整的阈值。从赋值中,它不直接获取初始容量值,而是调用另一个方法tableSizeFor()来计算阈值。

静态最终int tableSizeFor(Int Cap){int n=cap-1;n|=n>;>;>;1;n|=n>;>;>;2;n|=n>;>;>;4;n|=n>;>;>;8;n|=n>;>;>;16;return(n<;0。最大容量:N+1;}。

计算值将类似于2^N,它允许查找旧数据在新表中的位置。

当10000是初始容量时,我们可能认为它会在添加10000*0.75=7500个条目后触发大小调整。事实是,在运行tableSizeFor()之后,它调整大小的阈值变为2^14=16384。这是否意味着即使在Hashmap中添加了10000个条目,它也不会触发调整大小?

如果1000是初始容量怎么办?计算后的阈值将是2^10=1024,因此在添加1000个条目后也不会触发调整大小?

要理解这一点,还有另一个问题需要回答,表何时初始化?resize()的目的是在达到阈值时调整表的大小,但它还有另一个用途,resize()还负责表的初始化。

当使用构造函数方法创建HashMap对象时,它只会在将第一个值放入映射时初始化表。

Final V putVal(int hash,K key,Vvalue,Boolean Only IfAbsend,Boolean Eevict){Node<;K,V>;[]tab;Node<;K,V>;p;int n,i;if((tab=table)==null||(n=tab.length)=0)n=(tab=resize()).length;//调用resize()//.}。

最终节点<;K,V&>;[]Resize(){Node<;K,V&>;[]oldTab=table;int oldCap=(oldTab==null)?0:oldTab.length;int oldThr=Threshold;int NewCap,newThr=0;IF(oldCap&>0){if(oldCap&>t;=Maximum_Capacity){Threshold=Integer.MAX。Maximum_Capacity&;&;oldCap&>;=Default_Initial_Capacity)newThr=oldThr;<;1;}Else If(oldThr>;0)NewCap=oldThr;//1 Else{NewCap=Default_Initial_Capacity;newThr=(Int)(Default_Load_factor*Default_Initial_Capacity);}If(newThr=0){//2 Float ft=(Float)NewCap。FT<;(Float)Maximum_Capacity?(Int)ft:Integer.MAX_VALUE);}Threshold=newThr;//3 Node<;K,V>;[]newTab=(Node<;K,V>;[])new Node[NewCap];table=newTab;//4//.}。

当指定了初始容量但表尚未初始化时,它会将oldThr分配给NewCap,因此创建的新表将具有初始阈值的大小。那么门槛将调整为NewCap*0.75.。在此之后,将创建并初始化表。

因此,如果初始容量为1000,则触发调整大小的实际阈值将为1024*0.75=768,这意味着将在添加1000个条目之前触发调整大小。如果初始大小为10000,则触发调整大小的实际阈值为16384*0.75=12288。

因此,在创建HashMap对象时指定初始容量值时,要指定的实际值应该计算为预期的初始容量/负载因子,以避免意外的大小调整。