背景

也许你并不知道的HashMap和HashTable区别_HashTable

 

面试官:说一下HashMap和Hashtable的区别吧?

面试者:1. HashMap是线程非安全的,Hashtable是线程安全的

2.HashMap比HashTable快

3.Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

面试官:HashMap是否支持null Key,null value?HashTable是否支持null Key,null value?

面试者:HashMap支持null key和null value,HashTable也支持null key和null value

面试官:为什么?

面试者:规定的

面试者内心:崩溃!我也不知道为什么!上面都是面试题背的。

面试官:回去等通知吧

上面的面试场景你是否经历过?这个问题也许会伤到很多面试者的心,那我们扒出来这两个的源码来细细看看!

HashMap为什么支持null键值?

1.我们知道HashMap的可以要做Hash处理的过程:

 /**
 * Computes key.hashCode() and spreads (XORs) higher bits of hash
 * to lower. Because the table uses power-of-two masking, sets of
 * hashes that vary only in bits above the current mask will
 * always collide. (Among known examples are sets of Float keys
 * holding consecutive whole numbers in small tables.) So we
 * apply a transform that spreads the impact of higher bits
 * downward. There is a tradeoff between speed, utility, and
 * quality of bit-spreading. Because many common sets of hashes
 * are already reasonably distributed (so don't benefit from
 * spreading), and because we use trees to handle large sets of
 * collisions in bins, we just XOR some shifted bits in the
 * cheapest possible way to reduce systematic lossage, as well as
 * to incorporate impact of the highest bits that would otherwise
 * never be used in index calculations because of table bounds.
 */
 static final int hash(Object key) {
 int h;
 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 }

当key为null时,将key的值置为0计算

  1. Hashcode

值得分布情况:

 public final int hashCode() {
 return Objects.hashCode(key) ^ Objects.hashCode(value);
 }
/**
 * Returns the hash code of a non-{@code null} argument and 0 for
 * a {@code null} argument.
 *
 * @param o an object
 * @return the hash code of a non-{@code null} argument and 0 for
 * a {@code null} argument
 * @see Object#hashCode
 */
 public static int hashCode(Object o) {
 return o != null ? o.hashCode() : 0;
 }

当value为null时,默认为0

HashTable为什么不支持null键值

1.key不能为null,否则报空指针异常

 /**
 * Tests if some key maps into the specified value in this hashtable.
 * This operation is more expensive than the {@link #containsKey
 * containsKey} method.
 *
 * <p>Note that this method is identical in functionality to
 * {@link #containsValue containsValue}, (which is part of the
 * {@link Map} interface in the collections framework).
 *
 * @param value a value to search for
 * @return <code>true</code> if and only if some key maps to the
 * <code>value</code> argument in this hashtable as
 * determined by the <tt>equals</tt> method;
 * <code>false</code> otherwise.
 * @exception NullPointerException if the value is <code>null</code>
 */
 public synchronized boolean contains(Object value) {
 if (value == null) {
 throw new NullPointerException();
 }

 Entry<?,?> tab[] = table;
 for (int i = tab.length ; i-- > 0 ;) {
 for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
 if (e.value.equals(value)) {
 return true;
 }
 }
 }
 return false;
 }

2.value不能为null,否则报空指针异常

 /**
 * Maps the specified <code>key</code> to the specified
 * <code>value</code> in this hashtable. Neither the key nor the
 * value can be <code>null</code>. <p>
 *
 * The value can be retrieved by calling the <code>get</code> method
 * with a key that is equal to the original key.
 *
 * @param key the hashtable key
 * @param value the value
 * @return the previous value of the specified key in this hashtable,
 * or <code>null</code> if it did not have one
 * @exception NullPointerException if the key or value is
 * <code>null</code>
 * @see Object#equals(Object)
 * @see #get(Object)
 */
 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 = key.hashCode();
 int index = (hash & 0x7FFFFFFF) % tab.length;
 @SuppressWarnings("unchecked")
 Entry<K,V> entry = (Entry<K,V>)tab[index];
 for(; entry != null ; entry = entry.next) {
 if ((entry.hash == hash) && entry.key.equals(key)) {
 V old = entry.value;
 entry.value = value;
 return old;
 }
 }

 addEntry(hash, key, value, index);
 return null;
 }

jdk 为什么这么设计?

jdk的设计为什么没有保持一致?

首先,我们看一下HashTable

/ * @author Arthur van Hoff
 * @author Josh Bloch
 * @author Neal Gafter
 * @since JDK1.0
 */

它是jdk1.0的第一个版本出现的,主要设计者是Arthur van Hoff,也许设计者当时认为null作为key 和value 是没有什么用的。

再看一下HashMap的情况

/ * @author Doug Lea
 * @author Josh Bloch
 * @author Arthur van Hoff
 * @author Neal Gafter
 * @since 1.2
 */

它是直到jdk 1.2 才出现,主要设计者是大名鼎鼎的Doug Lea,实际项目中,真的是有value为null的情况的。key为null的情况比较少见,但不代表没有。HashMap允许null为key和value应当是类的设计者思考让这个类更有用的设计吧!