1 HashTable 结构(jdk 1.80_201)
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
2 HashTable特点
- key不能为null(因为代码中需要获取key的hashCode值,不然会报空指针。)
- value不能为null,因为代码中做了if判断,如果为null那就抛出空指针
- 方法是同步的
- 效率相对HashMap 慢
3 整体的存储结构
- 结构组成:数组+单向链表
4
链表节点
private static class Entry<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Entry<K,V> next; }
5 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; @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; }
- 方法加了同步锁。
- 逻辑:
- 根据key找到数组所在的索引
- for循环遍历链表,如果发现key相同就覆盖对应的value值,且return 之前key对应的value 值。
- 否则 添加新的元素
6 添加新元素 private void addEntry(int hash, K key, V value, int index) { modCount++;//修改Map的此时+1 Entry<?,?> tab[] = table;//拿到数组 if (count >= threshold) {// 数量是否大于临界值(容积量) // Rehash the table if the threshold is exceeded rehash(); //重新构建数组结构 tab = table; hash = key.hashCode(); index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++; }
逻辑:1 判断是否扩容 . 2 new 一个链表节点Entry,且把他的next属性设置为 数组所在index(根据key得到的索引位置)的第一个链表节点。3 新的链表节点成为了数组所在index位置的第一个链表节点
7 get方法
public synchronized V get(Object key) {//也是同步方法 Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length;//根据key得到的hash值获取在数组中的索引 for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { //循环该索引所在位置的链表结构. if ((e.hash == hash) && e.key.equals(key)) { return (V)e.value; } } return null; }
8 contains 方法(containsValue方法调用的是contains方法,也就是说两个方法一模一样的)
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; }
9 containsKey
public synchronized boolean containsKey(Object key) { Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return true; } } return false; }
逻辑:根据key找到hash,根据hash找到数组中的索引index,然后根据index找到链表,且循环这个单向链表。
结论:
1 HashTabel 会把新的元素添加到索引对应数组所在链表的第一个位置,也就是说新的元素不会像HashMap一样放置再链表最后面,而是在最前面。
2 没有发现红黑树结构
3 put,get,contains等方法都是同步方法
4 key,value不能为null
5 用于数据量不大(因为效率慢,数据量大的话插入所需时间会很长),且需要同步的方法。