◆
Hashtable简介
◆
推荐查看此篇文章之前首先查看 HashMap源码分析 效果更佳哦
与HashMap的数组加链表加红黑树不同,Hashtable在jdk 1.8中仅仅使用了数组+链表的方式进行存储。
◆
HashMap的属性
◆
HashMap的一些基础属性:
/** * 保村数据的数组 */ private transient Entry<?, ?>[] table;
/** * 数组的元素数量 */ private transient int count;
/** * 扩容阈值 * * @serial */ private int threshold;
/** * 加载因子 */ private float loadFactor;
/** * 被修改的次数 */ private transient int modCount = 0;
/** * use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = 1421746759512286392L; /** *最大容量 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
HashMap存储数据所使用的链表数据结构:
private static class Entry<K, V> implements Map.Entry<K, V> { final int hash; final K key; V value; Hashtable.Entry<K, V> next;
protected Entry(int hash, K key, V value, Hashtable.Entry<K, V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } }
◆
Hashtable的构造方法
◆
/** * 指定初始容量和加载因子 */ public Hashtable(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load: " + loadFactor);
if (initialCapacity == 0) initialCapacity = 1; this.loadFactor = loadFactor; table = new Entry<?, ?>[initialCapacity]; threshold = (int) Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); }
/** * 指定初始容量,加载因子使用默认的0.75 */ public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); }
/** * 默认构造方法 */ public Hashtable() { this(11, 0.75f); }
/** * 添加一个map */ public Hashtable(Map<? extends K, ? extends V> t) { this(Math.max(2 * t.size(), 11), 0.75f); putAll(t); }
◆
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; //计算key的哈希值 int hash = key.hashCode(); //计算key应该放的索引 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; } private void addEntry(int hash, K key, V value, int index) { //修改次数增加 modCount++;
Entry<?, ?> tab[] = table; //判断是否需要扩容 if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash();
tab = table; //对本次添加的元素重新计算hash 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++; }
◆
Hashtable的扩容
◆
/** * 扩容方法 */ @SuppressWarnings("unchecked") protected void rehash() { int oldCapacity = table.length; Entry<?, ?>[] oldMap = table;
// 尝试扩容2倍再+1 int newCapacity = (oldCapacity << 1) + 1; //如果扩容后大于最大数组大小,就用最大数组大小 if (newCapacity - MAX_ARRAY_SIZE > 0) { if (oldCapacity == MAX_ARRAY_SIZE) // Keep running with MAX_ARRAY_SIZE buckets return; newCapacity = MAX_ARRAY_SIZE; } Entry<?, ?>[] newMap = new Entry<?, ?>[newCapacity];
modCount++; // 计算新的扩容阈值 threshold = (int) Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); table = newMap; // 转移原结构中数据到新结构中 for (int i = oldCapacity; i-- > 0; ) { for (Entry<K, V> old = (Entry<K, V>) oldMap[i]; old != null; ) { Entry<K, V> e = old; old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = (Entry<K, V>) newMap[index]; newMap[index] = e; } } }
◆
Hashtable的查找
◆
/** * 查找方法 */ @SuppressWarnings("unchecked") public synchronized V get(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 (V) e.value; } } return null; }
查找时先定位键值对所在的桶的位置,然后再对链表或红黑树进行查找
◆
Hashtable的删除
◆
/** * 删除方法 */ public synchronized V remove(Object key) { Entry<?, ?> tab[] = table; //计算hash int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K, V> e = (Entry<K, V>) tab[index]; //遍历链表删除 for (Entry<K, V> prev = null; e != null; prev = e, e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { modCount++; if (prev != null) { prev.next = e.next; } else { tab[index] = e.next; } count--; V oldValue = e.value; e.value = null; return oldValue; } } return null; }
看了Hashtable的增删改查方法你就明白了为什么他是线程安全的了这哥们的方法都是使用synchronize修饰的。
鉴于篇幅有限,本篇文章仅列出上方部分代码,Hashtable完整源码解析请点击下方“阅读原文”查看!!!