HashMap
版本:1.8
基本特徵:
繼承的類:
AbstractMap<K,V>
實現的接口:Map <K,V>
繼承的類:Object
直接子類:ConcurrentHashMap , ConcurrentSkipListMap , EnumMap , HashMap , IdentityHashMap , TreeMap , WeakHashMap
實現的接口:
Serializable , Cloneable , Map <K,V>
直接子類:
LinkedHashMap , PrinterStateReasons ,還有自己寫的繼承自hashmap的類。
-
HashMap<K,V>是繼承自AbstractMap<K,V>的一個類。
-
存儲的元素是鍵值對<K,V>。
-
存儲的元素是無序的。
-
存儲的元素允許鍵值對爲空,但是值允許一個鍵爲空,多個值爲空。
-
不是線程安全的。
關於4:看程序說明問題
HashMap<String,String> map01 = new HashMap<String,String>(10);
String key01=null;String value01=null;
String key02=null;String value02=null;
//存入鍵和值都爲空
map01.put(key01,value01);
map01.put(key02,value02);
System.out.println(map01.size());
//存入鍵空,值不空
String key03=null;String value03="鍵空值不空value03";
map01.put(key03,value03);
System.out.println(map01.size());
//存入鍵空,值不空
String key04=null;String value04="鍵空值不空value04";
map01.put(key04,value04);
System.out.println(map01.size());
System.out.println(map01.get(key01));
System.out.println(map01.get(key02));
System.out.println(map01.get(key03));
System.out.println(map01.get(key04));
1
1
1
鍵空值不空value04
鍵空值不空value04
鍵空值不空value04
鍵空值不空value04
這是因爲,HashMap雖然允許空值,但是對於鍵,只允許一個空值存進去,之後再遇到鍵是空值的,相當於是修改空鍵所對應的value,所以上述程序存入四個鍵爲空的鍵值對,其實就是存入一個鍵,一個空值,所以HashMap的size一直是1。之後再遇到鍵是空的,就直接把之前存入的空鍵對應的值改成又遇到的值。
稍微修改一下:
HashMap<String,String> map01 = new HashMap<String,String>(10);
String key01=null;String value01=null;
String key02=null;String value02=null;
//存入鍵和值都爲空
map01.put(key01,value01);
map01.put(key02,value02);
System.out.println(map01.size());
//存入鍵空,值不空
String key03="02";String value03=null;
map01.put(key03,value03);
System.out.println(map01.size());
//存入鍵空,值不空
String key04="01";String value04=null;
map01.put(key04,value04);
System.out.println(map01.size());
System.out.println(map01.get(key01));
System.out.println(map01.get(key02));
System.out.println(map01.get(key03));
System.out.println(map01.get(key04));
1
2
3
null
null
null
null
這是鍵不空,值空的情況。可以看出,HashMap是允許多個值爲空的,換句話說,HashMap只檢測鍵是否重複。
類的特徵:
構造方法:四個
-
HashMap()無參構造方法
-
HashMap(int initialCapacity)指定初始容量構造方法
-
HashMap(int initialCapacity, float loadFactor)指定初始容量和負載因子構造方法
-
HashMap(Map<? extends K, ? extends V> m)指定存儲元素構造方法
- 無參構造方法public HashMap()
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
這裏只給負載因子賦默認值0.75f【f代表0.75是浮點型數】
- HashMap(int initialCapacity)指定初始容量構造方法
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
this指的是指定初始容量和負載因子構造方法,這裏調用了HashMap(int initialCapacity, float loadFactor)並指定參數是初始容量和默認負載因子。
- HashMap(int initialCapacity, float loadFactor)指定初始容量和負載因子構造方法
public HashMap(int initialCapacity, float loadFactor) {
//判斷指定容量是不是負數,是就拋出非法參數異常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
//判斷指定容量是不是大於哈希表的最大容量,是的話就限制指定容量縮小成哈希表的最大容量
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//檢測負載因子,小於等於0或者不是數字就拋出非法參數異常
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
//經上述三個if檢測修改完後,給負載因子賦值,給閾值賦值。
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
這裏介紹最後一條語句中的tableSizeFor(initialCapacity)
tableSizeFor作用:給輸入的數返回一個大於等於該數並且是2的冪次方的數。即:輸入7,那麼返回一個8.
閾值=哈希表的容量*負載因子。
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;//把n或n右移一位的結果賦值給n
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
哪個數來舉例:cap=9,那麼n=8
整型數據佔四個字節,最後一個字節的二進制:0000 1000
第一次右移並運算:0000 1000|0000 0100=0000 1100
第二次右移並運算:0000 1100|0000 0011=0000 1111
第三次右移並運算: 0000 1111|0000 0000=0000 1111
第四次右移並運算: 0000 1111|0000 0000=0000 1111
…
可知最後n=15,返回的數據是15+1=16。
在這個構造函數中,把閾值設置成大於等於初始容量的2的冪次方
- HashMap(Map<? extends K, ? extends V> m)指定存儲元素構造方法
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;//設置負載因子成默認值
putMapEntries(m, false);
}
對於:putMapEntries(m, false)函數:
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {//如果s=0,函數就結束了,本來就是個空的hashmap所以什麼都不用幹
if (table == null) { // 要是本來的table是空的,
float ft = ((float)s / loadFactor) + 1.0F;//計算閾值+1
int t = ((ft < (float)MAXIMUM_CAPACITY) ?//對閾值做修改
(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)//如果計算的閾值比實際閾值大就重新給實際閾值賦值
threshold = tableSizeFor(t);//調用tableSizeFor,使實際閾值是2的冪次方
}
else if (s > threshold)//如果s比實際閾值大就重新調整table大小
resize();
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {//把指定map放入hashmap
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);
}
}
}
未完,,,