なぜHashMapの初期サイズが16なのか、なぜロードファクタのサイズが0.75なのか、この2つの値の選択にはどのような特徴がありますか.

6941 ワード

コンテンツ:https://blog.csdn.net/Dazhu233/article/details/79596584
1、まずHashMapの定義を見ます.
public class HashMap<K,V>extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable

HashMapはAbstractMapのサブクラスであり,Mapインタフェースを実現している.
2、HashMapの四つの構造方法
1、HashMap()
Constructs an empty HashMap with the default initial capacity (16) and the default load factor (0.75).

2、HashMap(int initialCapacity)
Constructs an empty HashMap with the specified initial capacity and the default load factor (0.75).

3、HashMap(int initialCapacity, float loadFactor)
Constructs an empty HashMap with the specified initial capacity and load factor.

4、HashMap(Map extends K,? extends V> m)
Constructs a new HashMap with the same mappings as the specified Map.

全部で4つの構造方法を示した.1.HashMap()はパラメータなしで、デフォルトの初期化サイズは16で、ロードファクタは0.75です.2.HashMap(int initialCapacity)初期化サイズを指定します.3.HashMap(int initialCapacity,float loadFactor)初期化サイズとロードファクタサイズを指定します.4.HashMap(Map
3、コード分析
1、初期化コード
public HashMap(int initialCapacity,float loadFactor){  
   if(initialCapacity<0) //       0,      
       throw new IllegalArgumentException("Illegal initial capacity: "  
       +initialCapacity);  
   if(initialCapacity>MAXIMUM_CAPACITY)//              
       initialCapacity=MAXIMUM_CAPACITY;  
   if(loadFactor <=0|| Float.isNaN(loadFactor)) //      0 1    
       throw new IllegalArgumentException("Illegal load factor: "  
       +loadFactor);  
   this.loadFactor =loadFactor;  
   //threshold                        ,threshold=initialCapacity*loadFactor
   //                    
   this.threshold=tableSizeFor(initialCapacity);  
}

2、putメソッドを呼び出すたびに検証を行い、拡張が必要かどうかを判断する
if (++size > threshold)  
           resize();

3、拡張コード
Node[] oldTab=table;    
int oldCap=(oldTab==null)?0:oldTab.length;//             0  
int oldThr=threshold; //            。  
int newCap,newThr=0; //          。  
if(oldCap>0){ //          ,                
   if(oldCap>= MAXIMUM_CAPACITY){//          
       threshold=Integer.MAX_VALUE;  
       return oldTab;  
   }else if((newCap=oldCap<<1)=DEFALT_INITAL_CAPACITY)  
       newThr=oldThr<<1;   //                   ,  
                            //              2  
}else if(oldThr>0)    
   newCap=oldThr; //                    
if(newThr==0){  //                             
   float ft=(float)newCap*loadFactor;  
   newThr=(newCapfloat)MAXIMUM_CAPACITY?  
           (int)ft:Integer.MAX_VALUE);  
}

4、なぜデフォルト初期化バケツ配列サイズが16なのか、なぜロードファクタサイズが0.75なのか、この2つの値の選択にはどのような特徴があるのか
上記のコードを見ると、この2つの値が主に影響するthresholdの大きさがわかります.この値の数値は、現在のバケツ配列が拡張を必要としない境界の大きさです.
バケツ配列が拡張されるとメモリ領域が申請され、元のバケツの要素が新しいバケツ配列にコピーされることは、比較的時間がかかるプロセスであることはよく知られています.それなら、なぜこの2つの値を大きく設定しないのでしょうか.thresholdは2つの数の積であり、大きく設定すると容易に拡張されません.
なぜなら、バケツ初期化バケツ配列が大きすぎるとメモリスペースが浪費され、16は折衷の大きさで、1、2、3のようにいくつかの要素を入れて拡張することも、数千万のようにわずかな空間だけを利用して大量の浪費をもたらすこともないからだ.
ロードファクタが0.75に設定されているのは、1ではなく0.75に設定されているためです.設定が大きすぎると、バケツのキー値が衝突する確率が高くなり、同じバケツの位置にいくつかのvalue値が格納される可能性があります.そうすると、検索時間が増加し、性能が低下し、設定が小さすぎても適切ではありません.0.1であれば、10バケツ、thresholdが1で、2つのキー値ペアを置くと容量が広がり、スペースがもったいないです.