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 用于数据量不大(因为效率慢,数据量大的话插入所需时间会很长),且需要同步的方法。