Hashtable
Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable{
}
这个类实现了一个键值哈希表,其中Key值对象的类要实现hashCode和equals方法。
哈希表有两个重要的参数:初始容量值(initial capacity )和装填因子(load factor),容量值是哈希表中桶(buckets)的数量,容量值默认值为11,装填因子默认值为0.75。哈希表解决哈希碰撞的方法:每个桶中存储多个实体,这些实体需要顺序搜索。当哈希表中的实体数量与容量的比值超过装填因子时,哈希表将自动扩容。
注意:哈希表的容量值一般为质数
装填因子设置为0.75是对时间和空间消费的折中选择。设置太高,则增加时间花费;设置太低,则浪费空间。当哈希表扩容时,需要对原有哈希表中的实体进行重新哈希,会有时间消耗,如果初始容量设置 capacity=maxnum/loadfator ,则可以避免哈希表扩容。初始容量不宜太大,否则会浪费空间,但是设置足够大总比不断扩容重哈希要好很多。
The iterators returned by the iterator method of the collections returned by all of this class’s “collection view methods” are fail-fast.
fail-fast机制:当Iterator操作时,集合中的元素发生结构性的改变(不是由于Iterator.remove方法引起的),Iterator就会抛出ConcurrentModificationException。在并发模式下,iterator会快速并且干净的失败,而不是在后面某个操作时具有任意的、具有冒险行为的、不确定的。Hashtable的枚举方法所返回的枚举对象不是fail-fast的。
fail-fast的行为不能得到保证,一般来说,在面对不同步的并发修改时是不可能做出任何保证的。itertaor的fail-fast应当只用于检测错误,而不是利用它使得程序的迭代行为运行正确。
Hashtable是同步的、线程安全的,如果程序不需要同步,可以使用HashMap代替。如果需要一个高并发的线程安全的类,则可以使用ConcurrentHashMap。
Hashtable的方法是加了synchronized关键词。
Hashtable成员变量
/**
* The hash table data.
* 使用Entry数组存储数据
*/
private transient Entry<?,?>[] table;
/**
* The total number of entries in the hash table.
*/
private transient int count;
/**
* The table is rehashed when its size exceeds this threshold. (The
* value of this field is (int)(capacity * loadFactor).)
*
*重新hash的阈值
* @serial
*/
private int threshold;
/**
* The load factor for the hashtable.
*装填因子
* @serial
*/
private float loadFactor;
/**
* The number of times this Hashtable has been structurally modified
* Structural modifications are those that change the number of entries in
* the Hashtable or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the Hashtable fail-fast. (See ConcurrentModificationException).
*
* Hashtable结构改变的次数,用于fail-fast机制
*/
private transient int modCount=0;
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*
* 分配数组的最大值
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
Hashtable方法
/**
* 构造函数,默认capacity=11,loadFactor=0.75;
* threshold最大值为Integer.MAX_VALUE-8+1;
*/
public Hashtable(int initialCapacity, float loadFactor);
public Hashtable(int initialCapacity);
public Hashtable();
/**
* 注意是两倍的t.size()
*/
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
public synchronized int size();
public synchronized boolean isEmpty();
public synchronized Enumeration<K> keys() {
return this.<K>getEnumeration(KEYS);
}
public synchronized Enumeration<V> elements() {
return this.<V>getEnumeration(VALUES);
/**
* 对每个桶中的链表进行遍历,所以要用到两个for循环
* 判断是否存在value值的键值对,比查找是否存在key值的键值对花费要高很多,因为key可以hash查找,而value只能挨个查找对比。
*/
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;
}
public boolean containsValue(Object value);
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;
}
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;
}
/**
* 以接近两倍的原有容量扩展。
* newCapacity = oldCapacity << 1 + 1; 两倍加1,也就是尽可能避免选择能被2整除的数。最多也就是MAX_ARRAY_SIZE.
* 在桶中链表插入的时候,插入在头部,头部也是存储有值的。
*/
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
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;
//看懂这一点,和C++中的指针不同,这里没有专门的头指针。
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
/**
*private函数,在后面几个方法中会调用。
*添加一个Entry,Entry类将在稍后介绍
*Entry成员变量包含有:hash,key,value,next。
*/
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 = 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中有key值所对应的键值对,则更新key所对应的valu,否则创建并添加一个新的Entry。
* key 和 value都不能为null。当key为null时,key.hashCode()抛异常,当value为null时,检查并抛异常。
*/
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;
}
/**
*注意删除时,要有个保存前一个结点的位置,所以有个prev变量。另外还要注意,删除的是数组中的元素还是数组内链表的元素。
*
*/
public synchronized V remove(Object key) {
Entry<?,?> tab[] = table;
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;
}
public synchronized void putAll;
/**
*清除的时候要对数组内的每个对象进行清除。
*/
public synchronized void clear() {
Entry<?,?> tab[] = table;
modCount++;
for (int index = tab.length; --index >= 0; )
tab[index] = null;
count = 0;
}
/**
*注意这里是深拷贝。
*请注意比浅拷贝和深拷贝的区别。
*/
public synchronized Object clone() {
try {
Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
t.table = new Entry<?,?>[table.length];
for (int i = table.length ; i-- > 0 ; ) {
t.table[i] = (table[i] != null)
? (Entry<?,?>) table[i].clone() : null;
}
t.keySet = null;
t.entrySet = null;
t.values = null;
t.modCount = 0;
return t;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
/**
* 注意String的连接操作尽量用StringBuilder.
*/
public synchronized String toString() {
int max = size() - 1;
if (max == -1)
return "{}";
StringBuilder sb = new StringBuilder();
Iterator<Map.Entry<K,V>> it = entrySet().iterator();
sb.append('{');
for (int i = 0; ; i++) {
Map.Entry<K,V> e = it.next();
K key = e.getKey();
V value = e.getValue();
sb.append(key == this ? "(this Map)" : key.toString());
sb.append('=');
sb.append(value == this ? "(this Map)" : value.toString());
if (i == max)
return sb.append('}').toString();
sb.append(", ");
}
}
/**
*type是用于区分返回的是key还是value的枚举
*/
private <T> Enumeration<T> getEnumeration(int type) {
if (count == 0) {
return Collections.emptyEnumeration();
} else {
return new Enumerator<>(type, false);
}
}
private <T> Iterator<T> getIterator(int type) {
if (count == 0) {
return Collections.emptyIterator();
} else {
return new Enumerator<>(type, true);
}
}
/**
* Each of these fields are initialized to contain an instance of the
* appropriate view the first time this view is requested. The views are
* stateless, so there's no reason to create more than one of each.
* 有且只有一份,每次访问的时候返回的都是同一份。
*/
private transient volatile Set<K> keySet;
private transient volatile Set<Map.Entry<K,V>> entrySet;
private transient volatile Collection<V> values;
/**
* Returns a {@link Set} view of the keys contained in this map.
* The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. If the map is modified
* while an iteration over the set is in progress (except through
* the iterator's own <tt>remove</tt> operation), the results of
* the iteration are undefined. The set supports element removal,
* which removes the corresponding mapping from the map, via the
* <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
* <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
* operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
* operations.
*
*对于keySet、entrySet、values、iterator等删除、清空操作是会改变Hashtable中的数据,Hashtable中的数据改变时,这些视图中的数据也会改变。
*这些视图对象都是现成同步的。
* @since 1.2
*/
public Set<K> keySet() {
if (keySet == null)
keySet = Collections.synchronizedSet(new KeySet(), this);
return keySet;
}
private class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return getIterator(KEYS);
}
public int size() {
return count;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return Hashtable.this.remove(o) != null;
}
public void clear() {
Hashtable.this.clear();
}
}
/**
* equals方法三步:1,判断是否是自己;2,判断是否同一类型;3,判断内容是否相等。
*/
public synchronized boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map))
return false;
Map<?,?> t = (Map<?,?>) o;
if (t.size() != size())
return false;
try {
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Map.Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(t.get(key)==null && t.containsKey(key)))
return false;
} else {
if (!value.equals(t.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
public synchronized int hashCode() {
/*
* This code detects the recursion caused by computing the hash code
* of a self-referential hash table and prevents the stack overflow
* that would otherwise result. This allows certain 1.1-era
* applets with self-referential hash tables to work. This code
* abuses the loadFactor field to do double-duty as a hashCode
* in progress flag, so as not to worsen the space performance.
* A negative load factor indicates that hash code computation is
* in progress.
*/
int h = 0;
if (count == 0 || loadFactor < 0)
return h; // Returns zero
loadFactor = -loadFactor; // Mark hashCode computation in progress
Entry<?,?>[] tab = table;
for (Entry<?,?> entry : tab) {
while (entry != null) {
h += entry.hashCode();
entry = entry.next;
}
}
loadFactor = -loadFactor; // Mark hashCode computation complete
return h;
}
public synchronized V getOrDefault(Object key, V defaultValue) ;
/**
* 注意这里用到了modCount的值作为判断数据是否改动的依据。
*/
public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action); // explicit check required in case
// table is empty.
final int expectedModCount = modCount;
Entry<?, ?>[] tab = table;
for (Entry<?, ?> entry : tab) {
while (entry != null) {
action.accept((K)entry.key, (V)entry.value);
entry = entry.next;
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
}
}
}
public synchronized void replaceAll;
public synchronized V putIfAbsent;
public synchronized boolean remove;
...太多了,不一一介绍了。
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Entry<K,V> next;
...
private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
Entry<?,?>[] table = Hashtable.this.table;
int index = table.length;
Entry<?,?> entry;
Entry<?,?> lastReturned;
int type;
/**
* Indicates whether this Enumerator is serving as an Iterator
* or an Enumeration. (true -> Iterator).
*/
boolean iterator;
/**
* The modCount value that the iterator believes that the backing
* Hashtable should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
protected int expectedModCount = modCount;
Enumerator(int type, boolean iterator) {
this.type = type;
this.iterator = iterator;
}
public boolean hasMoreElements() {
Entry<?,?> e = entry;
int i = index;
Entry<?,?>[] t = table;
/* Use locals for faster loop iteration */
while (e == null && i > 0) {
e = t[--i];
}
entry = e;
index = i;
return e != null;
}
@SuppressWarnings("unchecked")
public T nextElement() {
Entry<?,?> et = entry;
int i = index;
Entry<?,?>[] t = table;
/* Use locals for faster loop iteration */
while (et == null && i > 0) {
et = t[--i];
}
entry = et;
index = i;
if (et != null) {
Entry<?,?> e = lastReturned = entry;
entry = e.next;
return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
}
throw new NoSuchElementException("Hashtable Enumerator");
}
// Iterator methods
public boolean hasNext() {
return hasMoreElements();
}
public T next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
return nextElement();
}
public void remove() {
if (!iterator)
throw new UnsupportedOperationException();
if (lastReturned == null)
throw new IllegalStateException("Hashtable Enumerator");
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
synchronized(Hashtable.this) {
Entry<?,?>[] tab = Hashtable.this.table;
int index = (lastReturned.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 == lastReturned) {
modCount++;
expectedModCount++;
if (prev == null)
tab[index] = e.next;
else
prev.next = e.next;
count--;
lastReturned = null;
return;
}
}
throw new ConcurrentModificationException();
}
}
}
Iterator,Set…等删除操作会影响到原有数据。Iterator在遍历的过程中,原有数据不能发生结构性的改变,否则抛出ConcurrentModificationException,但可以用自己的remove方法使原有数据发生改变。Enumeration没有这种限制,即使原有数据在Enumeration遍历时发生结构性的改变,Enumeration也不会抛出异常,但是遍历结果不太理想,无法把控。Iterator和Enumeration遍历的时候都是从数组的最后开始往前遍历的。
hash值的计算公式
(hashCode() & 0x7FFFFFFF)%Capacity
为什么要&上0x7FFFFFFF,因为要保证计算出来的值为正整数(32位),所以最高位要默认为0,防止溢出,表示是正整数。