我們都知道結論是:
HashMap可以允許插入null key和null value
HashTable和ConcurrentHashMap都不可以插入null key和null value
具體原因可以看下面的源碼:
首先是HashMap的put源碼:
- public V put(K key, V value) {
- if (table == EMPTY_TABLE) {
- inflateTable(threshold);
- }
- if (key == null)
- return putForNullKey(value);
- int hash = hash(key);
- int i = indexFor(hash, table.length);
- for (Entry<K,V> e = table[i]; e != null; e = e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
- modCount++;
- addEntry(hash, key, value, i);
- return null;
- }
null key的情況,調用putForNullKey方法,之前我在博客上已經寫過關於HashMap插入null key後的具體執行過程和源碼分析了。
下面是HashTable的源碼:
- public synchronized V put(K key, V value) {
- // Make sure the value is not null
- if (value == null) {
- throw new NullPointerException();
- }
- // Makes sure the key is not already in the hashtable.
- Entry tab[] = table;
- int hash = hash(key);
- int index = (hash & 0x7FFFFFFF) % tab.length;
- for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
- if ((e.hash == hash) && e.key.equals(key)) {
- V old = e.value;
- e.value = value;
- return old;
- }
- }
- modCount++;
- if (count >= threshold) {
- // Rehash the table if the threshold is exceeded
- rehash();
- tab = table;
- hash = hash(key);
- index = (hash & 0x7FFFFFFF) % tab.length;
- }
- // Creates the new entry.
- Entry<K,V> e = tab[index];
- tab[index] = new Entry<>(hash, key, value, e);
- count++;
- return null;
- }
源碼中判斷了null value的情況,null value就拋出空指針異常異常
但是我們並沒有看到判斷null key然後拋出異常的語句,那麼繼續看看hash方法的源碼就明白了:
- private int hash(Object k) {
- // hashSeed will be zero if alternative hashing is disabled.
- return hashSeed ^ k.hashCode();
- }
k.hashCode(),當k出入爲null key 就會報錯 空指針異常。
下面是ConcurrentHashMap的源碼:
- public V put(K key, V value) {
- Segment<K,V> s;
- if (value == null)
- throw new NullPointerException();
- int hash = hash(key);
- int j = (hash >>> segmentShift) & segmentMask;
- if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck
- (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
- s = ensureSegment(j);
- return s.put(key, hash, value, false);
- }
null value和null key 會報錯的問題和HashTable原因是一樣的,hash方法:
- private int hash(Object k) {
- int h = hashSeed;
- if ((0 != h) && (k instanceof String)) {
- return sun.misc.Hashing.stringHash32((String) k);
- }
- h ^= k.hashCode();
- // Spread bits to regularize both segment and index locations,
- // using variant of single-word Wang/Jenkins hash.
- h += (h << 15) ^ 0xffffcd7d;
- h ^= (h >>> 10);
- h += (h << 3);
- h ^= (h >>> 6);
- h += (h << 2) + (h << 14);
- return h ^ (h >>> 16);
- }