HashMap和Hashtable都位于java.util包中,实现机制基本差不多,现在一般建议使用HashMap,但也有一些区别,主要是对于null键值的处理。
它们都是基于hash原则,何为hashcode?简单说就是对变量/对象的属性应用某种算法后得到的一个唯一的串,用这个串来确定变量/对象的唯一性,一般多是在内存中的地址。都处理为一个数组,数组中都是存放的Entry链表,Entry(int hash, K key, V value, Entry<K,V> next),都是针对(key,value)的键值对处理,如果key一致,新值覆盖旧值。
区别如下:
1.Hashtable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap;
2.初始默认数组大小不一样,HashMap为16(MUST be a power of two),Hashtable为11,自动扩容方式为oldCapacity * 2 + 1;
3.Hashtable用的是Enumeration,旧框架的缘故,HashMap为Iterator;
4.Hashtable不允许键值为null的情况,HashMap允许(key,value为null均可以),主要操作就是put和get。
HashMap的put方法:
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); 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; }
首先判断key是否为空,为空则放入数据的table[0]位置,因为null的hashcode为0;
private V putForNullKey(V value) { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(0, null, value, 0); return null; }
key不为空,则根据hashcode算出新的hash(防止开发人员重写hashcode()方法,导致hashcode值过大过小等情况),然后算出在table数组的具体位置,遍历此table[i]链表,如果hash一样,key一致,新值覆盖旧值并返回旧值,否则在table[i]的链表头部新增一个Entry。
而Hashtable的put方法:
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; 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; index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. Entry<K,V> e = tab[index]; tab[index] = new Entry<K,V>(hash, key, value, e); count++; return null; }
此处如果value为null,则抛出异常,Hashtable中的hashcode并未经过任何算法加工,直接用的就是key的hashcode,然后根据(hash & 0x7FFFFFFF) % tab.length算出在数组中的具体位置,并将值放入。
HashMap的get()方法:
public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; }
如果已经了解HashMap的put()方法就很容易理解了。
Hashtable的get()方法:
public synchronized V get(Object key) { Entry tab[] = table; int hash = key.hashCode(); 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)) { return e.value; } } return null; }
理解也不难。
其他也有些方法的差别,你有我无,你无我有等等。