JDK源码阅读(5)(HashTable类阅读笔记)
HashTable
public class Hashtable
extends Dictionary
implements Map, Cloneable, java.io.Serializable {
...
}
HashMap
只实现了Map
接口,而HashTable
还继承了Dictionary
类。但实际上Dictionary
类只是一个历史遗留问题,任何新的键值对集合都只需要实现Map
接口。1. 构造方法
/**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
this(11, 0.75f);
}
HashTable
的默认容量是11,默认负载因子是0.75。HashMap
的这两个值分别是16和0.75。2. Properties类和Void类
在阅读到
HashTable
的构造函数时,我看到了一个奇怪的构造函数:/**
* A constructor chained from {@link Properties} keeps Hashtable fields
* uninitialized since they are not used.
*
* @param dummy a dummy parameter
*/
Hashtable(Void dummy) {}
首先,在参数列表中出现了一个我从来没见过的类:
Void
类。我们进入查看这个类的定义。package java.lang;
/**
* The {@code Void} class is an uninstantiable placeholder class to hold a
* reference to the {@code Class} object representing the Java keyword
* void.
*
* @authorunascribed
* @since1.1
*/
public final class Void {/**
* The {@code Class} object representing the pseudo-type corresponding to
* the keyword {@code void}.
*/
@SuppressWarnings("unchecked")
public static final Class TYPE = (Class) Class.getPrimitiveClass("void");
/*
* The Void class cannot be instantiated.
*/
private Void() {}
}
可以看到,
Void
类是一个final类,同时只有一个私有的构造函数。显然,这个类既不可以被继承,也不可以被实例化。在doc中我们得知,这是一个占位符类,用于保存对表示Java关键字void
的Class
对象的引用。我们可以想到,假如将Void类作为一个方法的返回类型,这意味着这个方法只能返回null。
public Void f(){
...
}
那么在这里作为参数的
Void
类又起到怎样的功能呢?继续阅读构造方法上方的doc,我们发现,这个函数是供java.util.Properties
类使用的,Properties
类中有下面这个构造函数。private Properties(Properties defaults, int initialCapacity) {
// use package-private constructor to
// initialize unused fields with dummy values
super((Void) null);
map = new ConcurrentHashMap<>(initialCapacity);
this.defaults = defaults;
// Ensure writes can't be reordered
UNSAFE.storeFence();
}
Properties
类是HashTable
类的子类,在这个构造函数中它调用了HashTable
中包级私有的构造方法。如果没有这个super语句的话,就会默认调用父类HashTable
的无参构造函数,但显然,这是没有必要的。通过在HashTable
中添加一个伪构造方法供Properties
类调用,可以避免这种无必要的开销。这里顺便说明一下
Properties
类。- Properties类表示一组持久的属性。表示一个持久的属性集,属性列表中每个键及其对应值都是一个字符串。
- Properties 类被许多 Java 类使用。例如,在获取环境变量时它就作为 System.getProperties() 方法的返回值。
- Properties 定义如下实例变量.这个变量持有一个 Properties 对象相关的默认属性列表。
Properties defaults;
HashTable
中为几乎所有业务方法都添加了synchronized
关键字,实现了线程安全。public synchronized int size() {...}
public synchronized boolean isEmpty() {...}
public synchronized V put(K key, V value) {...}
public synchronized V get(Object key) {...}
public synchronized V remove(Object key) {...}
4. keys方法和elements方法
public synchronized Enumeration keys() {
return this.getEnumeration(KEYS);
}public synchronized Enumeration elements() {
return this.getEnumeration(VALUES);
}
返回了键集合和值集合的枚举。
private Enumeration getEnumeration(int type) {
if (count == 0) {
return Collections.emptyEnumeration();
} else {
return new Enumerator<>(type, false);
}
}
下面是作为
HashTable
类内部类的枚举类private class Enumerator implements Enumeration, Iterator {
final Entry,?>[] table = Hashtable.this.table;
int index = table.length;
Entry,?> entry;
Entry,?> lastReturned;
final int type;
/**
* Indicates whether this Enumerator is serving as an Iterator
* or an Enumeration.(true -> Iterator).
*/
final 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 = Hashtable.this.modCount;
Enumerator(int type, boolean iterator) {
this.type = type;
this.iterator = iterator;
}public boolean hasMoreElements() {
...
}@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 (Hashtable.this.modCount != expectedModCount)
throw new ConcurrentModificationException();
return nextElement();
}public void remove() {
...
}
}
5. HashTable中定位下标的方法
int index = (hash & 0x7FFFFFFF) % tab.length;
hash & 0x7FFFFFFF
是为了保证hash值是一个正数,即二进制下第一位是0。【JDK源码阅读(5)(HashTable类阅读笔记)】然后直接对
length
取模。6. rehash方法和addEntry方法
protected void rehash() {
int oldCapacity = table.length;
Entry,?>[] oldMap = table;
// 扩容逻辑:乘二加一
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// 原容量就满了,则直接返回
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 old = (Entry)oldMap[i] ;
old != null ;
) {
Entry e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry)newMap[index];
newMap[index] = e;
}
}
}
该方法会增加哈希表的容量并在内部重新组织哈希表。当哈希表中的键数超过此哈希表的容量*负载因子时,将自动调用此方法。扩容的逻辑是容量*2+1。
下面的方法用于向表中假如键值对,在put等方法中都有调用。
private void addEntry(int hash, K key, V value, int index) {
Entry,?> tab[] = table;
if (count >= threshold) {
// 先调用rehash()
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}// Creates the new entry.
@SuppressWarnings("unchecked")
Entry e = (Entry) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
modCount++;
}
推荐阅读
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- Ⅴ爱阅读,亲子互动——打卡第178天
- 上班后阅读开始变成一件奢侈的事
- 历史教学书籍
- 绘本讲师训练营【24期】14/21阅读原创《小黑鱼》
- 21天|21天|M&M《见识》04
- 绘本讲师训练营7期9/21阅读原创《蜗牛屋|绘本讲师训练营7期9/21阅读原创《蜗牛屋 》
- 桂妃研读社|桂妃研读社|D124|如何有效阅读一本书 Day1
- Android事件传递源码分析
- 4.23世界阅读日,樊登读书狂欢放送,听书中成长