一、Hashtable 概述
1、Hashtable是个古老的 Map 实现类,JDK1.0就提供了。不同于HashMap,Hashtable是线程安全的,效率较低。
2、Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用。
3、与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value。
4、与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序。
5、Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。
6、与 HashMap 类似,Hashtable 也是散列表的实现。它的内部结构可以理解为「数组 + 链表」的形式,结构示意图如下:
Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了。
二、Hashtable 类结构
1、Hashtable 类继承结构
2、Hashtable 类签名
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {}
Hashtable 的 key 和 value 都不能为空(HashMap 的 key 和 value 都允许为空),并且 key 必须要实现 hashCode 方法和 equals 方法。
PS: Hashtable 目前使用不是很多,若无线程安全的需求,推荐使用 HashMap;若需要线程安全的高并发实现,推荐使用 ConcurrentHashMap。
3、Hashtable 方法列表
三、Hashtable 成员变量
1
2 (1)Hashtable 内部存储元素的数组
3 /**
4 * The hash table data.
5 */
6 private transient Entry<?,?>[] table;
7
8 (2)元素的数量
9 /**
10 * The total number of entries in the hash table.
11 */
12 private transient int count;
13
14 (3)Hashtable 的阈值 (int)(capacity * loadFactor)
15 /**
16 * The table is rehashed when its size exceeds this threshold. (The
17 * value of this field is (int)(capacity * loadFactor).)
18 *
19 * @serial
20 */
21 private int threshold;
22
23 (4)负载因子(加载因子)
24 /**
25 * The load factor for the hashtable.
26 *
27 * @serial
28 */
29 private float loadFactor;
30
31 (5)记录对该 Hashtable 进行结构修改的次数,用于快速失败(fail-fast)
32 /**
33 * The number of times this Hashtable has been structurally modified
34 * Structural modifications are those that change the number of entries in
35 * the Hashtable or otherwise modify its internal structure (e.g.,
36 * rehash). This field is used to make iterators on Collection-views of
37 * the Hashtable fail-fast. (See ConcurrentModificationException).
38 */
39 private transient int modCount = 0;
40
41 (6)标识本类的唯一序列化ID
42 /** use serialVersionUID from JDK 1.0.2 for interoperability */
43 private static final long serialVersionUID = 1421746759512286392L;
44
45 (7)数组能够分配的最大容量
46 /**
47 * The maximum size of array to allocate.
48 * Some VMs reserve some header words in an array.
49 * Attempts to allocate larger arrays may result in
50 * OutOfMemoryError: Requested array size exceeds VM limit
51 */
52 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
53
54 (8) 三种视图的展现
55 // Views
56
57 /**
58 * Each of these fields are initialized to contain an instance of the
59 * appropriate view the first time this view is requested. The views are
60 * stateless, so there's no reason to create more than one of each.
61 */
62 private transient volatile Set<K> keySet;
63 private transient volatile Set<Map.Entry<K,V>> entrySet;
64 private transient volatile Collection<V> values;
65
66 (9)枚举迭代的类型
67 // Types of Enumerations/Iterations
68 private static final int KEYS = 0;
69 private static final int VALUES = 1;
70 private static final int ENTRIES = 2;
四、Hashtable 构造器
1、方式一
1 // 构造一个空的 Hashtable,初始容量为 11,负载因子为 0.75
2 /**
3 * Constructs a new, empty hashtable with a default initial capacity (11)
4 * and load factor (0.75).
5 */
6 public Hashtable() {
7 this(11, 0.75f);
8 }
2、方式二
1 //构造一个空的 Hashtable,指定初始容量,负载因子为 0.75
2 public Hashtable(int initialCapacity) {
3 this(initialCapacity, 0.75f);
4 }
3、方式三
1 // 构造一个空的 Hashtable,指定初始容量和负载因子
2 public Hashtable(int initialCapacity, float loadFactor) {
3 if (initialCapacity < 0)
4 throw new IllegalArgumentException("Illegal Capacity: "+
5 initialCapacity);
6 if (loadFactor <= 0 || Float.isNaN(loadFactor))
7 throw new IllegalArgumentException("Illegal Load: "+loadFactor);
8 if (initialCapacity==0)
9 initialCapacity = 1;
10 this.loadFactor = loadFactor;
11 table = new Entry<?,?>[initialCapacity];
12 threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
13 }
4、方式四
1 // 使用给定的 Map 构造一个 Hashtable
2 public Hashtable(Map<? extends K, ? extends V> t) {
3 this(Math.max(2*t.size(), 11), 0.75f);
4 putAll(t);
5 }
五、Hashtable 中 Entry 节点
Entry 类
1 /**
2 * Hashtable bucket collision list entry
3 */
4 private static class Entry<K,V> implements Map.Entry<K,V> {
5 final int hash;
6 final K key;
7 V value;
8 Entry<K,V> next;
9
10 protected Entry(int hash, K key, V value, Entry<K,V> next) {
11 this.hash = hash;
12 this.key = key;
13 this.value = value;
14 this.next = next;
15 }
16
17 @SuppressWarnings("unchecked")
18 protected Object clone() {
19 return new Entry<>(hash, key, value,
20 (next==null ? null : (Entry<K,V>) next.clone()));
21 }
22
23 // Map.Entry Ops
24
25 public K getKey() {
26 return key;
27 }
28
29 public V getValue() {
30 return value;
31 }
32
33 public V setValue(V value) {
34 if (value == null)
35 throw new NullPointerException();
36
37 V oldValue = this.value;
38 this.value = value;
39 return oldValue;
40 }
41
42 public boolean equals(Object o) {
43 if (!(o instanceof Map.Entry))
44 return false;
45 Map.Entry<?,?> e = (Map.Entry<?,?>)o;
46
47 return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
48 (value==null ? e.getValue()==null : value.equals(e.getValue()));
49 }
50
51 public int hashCode() {
52 return hash ^ Objects.hashCode(value);
53 }
54
55 public String toString() {
56 return key.toString()+"="+value.toString();
57 }
58 }
Entry 类实现了 Map.Entry 接口,是 Hashtable 中的节点类。
六、添加元素 put() 方法
1、添加单个元素 put() 方法
1 public synchronized V put(K key, V value) {
2 // Make sure the value is not null (value 不能为空)
3 if (value == null) {
4 throw new NullPointerException();
5 }
6
7 // Makes sure the key is not already in the hashtable.
8 Entry<?,?> tab[] = table;
9 // 计算 key 在 table 中的索引
10 int hash = key.hashCode();
11 int index = (hash & 0x7FFFFFFF) % tab.length;
12 // 判断 key 在 table 中是否已存在,若存在,则用 value 替换旧值
13 @SuppressWarnings("unchecked")
14 Entry<K,V> entry = (Entry<K,V>)tab[index];
15 for(; entry != null ; entry = entry.next) {
16 if ((entry.hash == hash) && entry.key.equals(key)) {
17 V old = entry.value;
18 entry.value = value;
19 return old;
20 }
21 }
22 // 若不存在,则执行 addEntry 方法,将 key-value 添加到 table
23 addEntry(hash, key, value, index);
24 return null;
25 }
可以看到,key 或 value 有一个为空都会抛出 NullPointerException 异常,因此二者都不能为空。
添加节点 addEntry()
1 private void addEntry(int hash, K key, V value, int index) {
2 modCount++;
3 Entry<?,?> tab[] = table;
4 if (count >= threshold) {
5 // Rehash the table if the threshold is exceeded
6 // 超过阈值,则扩容
7 rehash();
8 tab = table;
9 hash = key.hashCode();
10 index = (hash & 0x7FFFFFFF) % tab.length;
11 }
12 // Creates the new entry.
13 // 将 key-value 添加到 table 中(头插法,即插到链表的头部)
14 // 即:先拿到 index 位置的元素,若为空,表示插入 entry 后则只有一个元素;
15 // 若不为空,表示该位置已有元素,将已有元素 e 连接到新的 entry 后面
16 @SuppressWarnings("unchecked")
17 Entry<K,V> e = (Entry<K,V>) tab[index];
18 tab[index] = new Entry<>(hash, key, value, e);
19 count++;
20 }
值得注意的是,put 方法(包括后面分析的 get 和 remove 等方法)带有 synchronized 关键字,Hashtable 就是通过这种方式实现线程安全的。
这里锁定的是整个 table,因此并发效率较低,这也是高并发场景下推荐使用 ConcurrentHashMap 的原因。
在这里可以看到,这里的 hash 函数是直接使用 key.hashCode() 来获取的。
计算元素下标位置是:(hash & 0x7FFFFFFF) % tab.length 通过 hash值与上 Integer.MAX_VALUE-1 再与 table 的长度取余数以保证可以放到数组内。
2、添加一个 Map 集合
1 public synchronized void putAll(Map<? extends K, ? extends V> t) {
2 for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
3 put(e.getKey(), e.getValue());
4 }
七、获取元素 get() 方法
get() 方法
1 public synchronized V get(Object key) {
2 Entry<?,?> tab[] = table;
3 int hash = key.hashCode();
4 int index = (hash & 0x7FFFFFFF) % tab.length;
5 for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
6 if ((e.hash == hash) && e.key.equals(key)) {
7 return (V)e.value;
8 }
9 }
10 return null;
11 }
八、移除元素 remove() 方法
remove() 方法
1 public synchronized V remove(Object key) {
2 Entry<?,?> tab[] = table;
3 int hash = key.hashCode();
4 int index = (hash & 0x7FFFFFFF) % tab.length;
5 @SuppressWarnings("unchecked")
6 Entry<K,V> e = (Entry<K,V>)tab[index];
7 for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
8 if ((e.hash == hash) && e.key.equals(key)) {
9 modCount++;
10 if (prev != null) {
11 prev.next = e.next;
12 } else {
13 tab[index] = e.next;
14 }
15 count--;
16 V oldValue = e.value;
17 e.value = null;
18 return oldValue;
19 }
20 }
21 return null;
22 }
九、扩容方法
rehash() 扩容方法:
1 protected void rehash() {
2 int oldCapacity = table.length;
3 Entry<?,?>[] oldMap = table;
4 // overflow-conscious code
5 // 新容量为旧容量的 2 倍加 1
6 int newCapacity = (oldCapacity << 1) + 1;
7 // 若新容量的值超过最大容量 MAX_ARRAY_SIZE,且旧容量为 MAX_ARRAY_SIZE,则直接返回;
8 // 若旧容量值不为 MAX_ARRAY_SIZE,则新容量为 MAX_ARRAY_SIZE.
9 if (newCapacity - MAX_ARRAY_SIZE > 0) {
10 if (oldCapacity == MAX_ARRAY_SIZE)
11 // Keep running with MAX_ARRAY_SIZE buckets
12 return;
13 newCapacity = MAX_ARRAY_SIZE;
14 }
15 // 新建一个 Entry 数组,容量为上面计算的容量大小
16 Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
17 modCount++;
18 threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
19 table = newMap;
20 for (int i = oldCapacity ; i-- > 0 ;) {
21 for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
22 Entry<K,V> e = old;
23 old = old.next;
24 int index = (e.hash & 0x7FFFFFFF) % newCapacity;
25 // 注意这里会调换顺序
26 e.next = (Entry<K,V>)newMap[index];
27 newMap[index] = e;
28 }
29 }
30 }
扩容操作,若 index 位置为链表,且插入顺序为 1、2、3,则在该位置的存储顺序为 3、2、1。扩容时,会从前往后读取元素并操作,因此扩容后的顺序为 3、2、1。示意图:
十、遍历(三种集合视图 EntrySet、keySet 和 values)
三种集合视图 EntrySet、keySet 和 values
1 private transient volatile Set<K> keySet;
2 private transient volatile Set<Map.Entry<K,V>> entrySet;
3 private transient volatile Collection<V> values;
4
5 public Set<K> keySet() {
6 if (keySet == null)
7 keySet = Collections.synchronizedSet(new KeySet(), this);
8 return keySet;
9 }
10
11 public Set<Map.Entry<K,V>> entrySet() {
12 if (entrySet==null)
13 entrySet = Collections.synchronizedSet(new EntrySet(), this);
14 return entrySet;
15 }
16
17 public Collection<V> values() {
18 if (values==null)
19 values = Collections.synchronizedCollection(new ValueCollection(),
20 this);
21 return values;
22 }
十一、克隆与序列化
1、克隆
1 public synchronized Object clone() {
2 try {
3 Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
4 t.table = new Entry<?,?>[table.length];
5 for (int i = table.length ; i-- > 0 ; ) {
6 t.table[i] = (table[i] != null)
7 ? (Entry<?,?>) table[i].clone() : null;
8 }
9 t.keySet = null;
10 t.entrySet = null;
11 t.values = null;
12 t.modCount = 0;
13 return t;
14 } catch (CloneNotSupportedException e) {
15 // this shouldn't happen, since we are Cloneable
16 throw new InternalError(e);
17 }
18 }
2、序列化和反序列化
1 private void writeObject(java.io.ObjectOutputStream s)
2 throws IOException {
3 Entry<Object, Object> entryStack = null;
4
5 synchronized (this) {
6 // Write out the threshold and loadFactor
7 s.defaultWriteObject();
8
9 // Write out the length and count of elements
10 s.writeInt(table.length);
11 s.writeInt(count);
12
13 // Stack copies of the entries in the table
14 for (int index = 0; index < table.length; index++) {
15 Entry<?,?> entry = table[index];
16
17 while (entry != null) {
18 entryStack =
19 new Entry<>(0, entry.key, entry.value, entryStack);
20 entry = entry.next;
21 }
22 }
23 }
24
25 // Write out the key/value objects from the stacked entries
26 while (entryStack != null) {
27 s.writeObject(entryStack.key);
28 s.writeObject(entryStack.value);
29 entryStack = entryStack.next;
30 }
31 }
32
33 /**
34 * Reconstitute the Hashtable from a stream (i.e., deserialize it).
35 */
36 private void readObject(java.io.ObjectInputStream s)
37 throws IOException, ClassNotFoundException
38 {
39 // Read in the threshold and loadFactor
40 s.defaultReadObject();
41
42 // Validate loadFactor (ignore threshold - it will be re-computed)
43 if (loadFactor <= 0 || Float.isNaN(loadFactor))
44 throw new StreamCorruptedException("Illegal Load: " + loadFactor);
45
46 // Read the original length of the array and number of elements
47 int origlength = s.readInt();
48 int elements = s.readInt();
49
50 // Validate # of elements
51 if (elements < 0)
52 throw new StreamCorruptedException("Illegal # of Elements: " + elements);
53
54 // Clamp original length to be more than elements / loadFactor
55 // (this is the invariant enforced with auto-growth)
56 origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);
57
58 // Compute new length with a bit of room 5% + 3 to grow but
59 // no larger than the clamped original length. Make the length
60 // odd if it's large enough, this helps distribute the entries.
61 // Guard against the length ending up zero, that's not valid.
62 int length = (int)((elements + elements / 20) / loadFactor) + 3;
63 if (length > elements && (length & 1) == 0)
64 length--;
65 length = Math.min(length, origlength);
66
67 if (length < 0) { // overflow
68 length = origlength;
69 }
70
71 // Check Map.Entry[].class since it's the nearest public type to
72 // what we're actually creating.
73 SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, length);
74 table = new Entry<?,?>[length];
75 threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
76 count = 0;
77
78 // Read the number of elements and then all the key/value objects
79 for (; elements > 0; elements--) {
80 @SuppressWarnings("unchecked")
81 K key = (K)s.readObject();
82 @SuppressWarnings("unchecked")
83 V value = (V)s.readObject();
84 // sync is eliminated for performance
85 reconstitutionPut(table, key, value);
86 }
87 }
88
89 private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
90 throws StreamCorruptedException
91 {
92 if (value == null) {
93 throw new java.io.StreamCorruptedException();
94 }
95 // Makes sure the key is not already in the hashtable.
96 // This should not happen in deserialized version.
97 int hash = key.hashCode();
98 int index = (hash & 0x7FFFFFFF) % tab.length;
99 for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
100 if ((e.hash == hash) && e.key.equals(key)) {
101 throw new java.io.StreamCorruptedException();
102 }
103 }
104 // Creates the new entry.
105 @SuppressWarnings("unchecked")
106 Entry<K,V> e = (Entry<K,V>)tab[index];
107 tab[index] = new Entry<>(hash, key, value, e);
108 count++;
109 }
十二、其他常用方法
1 // 获取元素数量
2 public synchronized int size() {
3 return count;
4 }
5
6 //集合是否为空
7 public synchronized boolean isEmpty() {
8 return count == 0;
9 }
10
11 //判断是否包含某个 value
12 public synchronized boolean contains(Object value) {
13 if (value == null) {
14 throw new NullPointerException();
15 }
16
17 Entry<?,?> tab[] = table;
18 for (int i = tab.length ; i-- > 0 ;) {
19 for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
20 if (e.value.equals(value)) {
21 return true;
22 }
23 }
24 }
25 return false;
26 }
27
28 //判断是否包含某个 value
29 public boolean containsValue(Object value) {
30 return contains(value);
31 }
32
33 //判断是否包含某个 key
34 public synchronized boolean containsKey(Object key) {
35 Entry<?,?> tab[] = table;
36 int hash = key.hashCode();
37 int index = (hash & 0x7FFFFFFF) % tab.length;
38 for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
39 if ((e.hash == hash) && e.key.equals(key)) {
40 return true;
41 }
42 }
43 return false;
44 }
45
46 //清空集合
47 public synchronized void clear() {
48 Entry<?,?> tab[] = table;
49 modCount++;
50 for (int index = tab.length; --index >= 0; )
51 tab[index] = null;
52 count = 0;
53 }
十三、JDK8 新增方法
Hashtable 对 Map 中默认方法的重写实现:
1 @Override
2 public synchronized V getOrDefault(Object key, V defaultValue) {
3 V result = get(key);
4 return (null == result) ? defaultValue : result;
5 }
6
7 @SuppressWarnings("unchecked")
8 @Override
9 public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
10 Objects.requireNonNull(action); // explicit check required in case
11 // table is empty.
12 final int expectedModCount = modCount;
13
14 Entry<?, ?>[] tab = table;
15 for (Entry<?, ?> entry : tab) {
16 while (entry != null) {
17 action.accept((K)entry.key, (V)entry.value);
18 entry = entry.next;
19
20 if (expectedModCount != modCount) {
21 throw new ConcurrentModificationException();
22 }
23 }
24 }
25 }
26
27 @SuppressWarnings("unchecked")
28 @Override
29 public synchronized void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
30 Objects.requireNonNull(function); // explicit check required in case
31 // table is empty.
32 final int expectedModCount = modCount;
33
34 Entry<K, V>[] tab = (Entry<K, V>[])table;
35 for (Entry<K, V> entry : tab) {
36 while (entry != null) {
37 entry.value = Objects.requireNonNull(
38 function.apply(entry.key, entry.value));
39 entry = entry.next;
40
41 if (expectedModCount != modCount) {
42 throw new ConcurrentModificationException();
43 }
44 }
45 }
46 }
47
48 @Override
49 public synchronized V putIfAbsent(K key, V value) {
50 Objects.requireNonNull(value);
51
52 // Makes sure the key is not already in the hashtable.
53 Entry<?,?> tab[] = table;
54 int hash = key.hashCode();
55 int index = (hash & 0x7FFFFFFF) % tab.length;
56 @SuppressWarnings("unchecked")
57 Entry<K,V> entry = (Entry<K,V>)tab[index];
58 for (; entry != null; entry = entry.next) {
59 if ((entry.hash == hash) && entry.key.equals(key)) {
60 V old = entry.value;
61 if (old == null) {
62 entry.value = value;
63 }
64 return old;
65 }
66 }
67
68 addEntry(hash, key, value, index);
69 return null;
70 }
71
72 @Override
73 public synchronized boolean remove(Object key, Object value) {
74 Objects.requireNonNull(value);
75
76 Entry<?,?> tab[] = table;
77 int hash = key.hashCode();
78 int index = (hash & 0x7FFFFFFF) % tab.length;
79 @SuppressWarnings("unchecked")
80 Entry<K,V> e = (Entry<K,V>)tab[index];
81 for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
82 if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
83 modCount++;
84 if (prev != null) {
85 prev.next = e.next;
86 } else {
87 tab[index] = e.next;
88 }
89 count--;
90 e.value = null;
91 return true;
92 }
93 }
94 return false;
95 }
96
97 @Override
98 public synchronized boolean replace(K key, V oldValue, V newValue) {
99 Objects.requireNonNull(oldValue);
100 Objects.requireNonNull(newValue);
101 Entry<?,?> tab[] = table;
102 int hash = key.hashCode();
103 int index = (hash & 0x7FFFFFFF) % tab.length;
104 @SuppressWarnings("unchecked")
105 Entry<K,V> e = (Entry<K,V>)tab[index];
106 for (; e != null; e = e.next) {
107 if ((e.hash == hash) && e.key.equals(key)) {
108 if (e.value.equals(oldValue)) {
109 e.value = newValue;
110 return true;
111 } else {
112 return false;
113 }
114 }
115 }
116 return false;
117 }
118
119 @Override
120 public synchronized V replace(K key, V value) {
121 Objects.requireNonNull(value);
122 Entry<?,?> tab[] = table;
123 int hash = key.hashCode();
124 int index = (hash & 0x7FFFFFFF) % tab.length;
125 @SuppressWarnings("unchecked")
126 Entry<K,V> e = (Entry<K,V>)tab[index];
127 for (; e != null; e = e.next) {
128 if ((e.hash == hash) && e.key.equals(key)) {
129 V oldValue = e.value;
130 e.value = value;
131 return oldValue;
132 }
133 }
134 return null;
135 }
136
137 @Override
138 public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
139 Objects.requireNonNull(mappingFunction);
140
141 Entry<?,?> tab[] = table;
142 int hash = key.hashCode();
143 int index = (hash & 0x7FFFFFFF) % tab.length;
144 @SuppressWarnings("unchecked")
145 Entry<K,V> e = (Entry<K,V>)tab[index];
146 for (; e != null; e = e.next) {
147 if (e.hash == hash && e.key.equals(key)) {
148 // Hashtable not accept null value
149 return e.value;
150 }
151 }
152
153 V newValue = mappingFunction.apply(key);
154 if (newValue != null) {
155 addEntry(hash, key, newValue, index);
156 }
157
158 return newValue;
159 }
160
161 @Override
162 public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
163 Objects.requireNonNull(remappingFunction);
164
165 Entry<?,?> tab[] = table;
166 int hash = key.hashCode();
167 int index = (hash & 0x7FFFFFFF) % tab.length;
168 @SuppressWarnings("unchecked")
169 Entry<K,V> e = (Entry<K,V>)tab[index];
170 for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
171 if (e.hash == hash && e.key.equals(key)) {
172 V newValue = remappingFunction.apply(key, e.value);
173 if (newValue == null) {
174 modCount++;
175 if (prev != null) {
176 prev.next = e.next;
177 } else {
178 tab[index] = e.next;
179 }
180 count--;
181 } else {
182 e.value = newValue;
183 }
184 return newValue;
185 }
186 }
187 return null;
188 }
189
190 @Override
191 public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
192 Objects.requireNonNull(remappingFunction);
193
194 Entry<?,?> tab[] = table;
195 int hash = key.hashCode();
196 int index = (hash & 0x7FFFFFFF) % tab.length;
197 @SuppressWarnings("unchecked")
198 Entry<K,V> e = (Entry<K,V>)tab[index];
199 for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
200 if (e.hash == hash && Objects.equals(e.key, key)) {
201 V newValue = remappingFunction.apply(key, e.value);
202 if (newValue == null) {
203 modCount++;
204 if (prev != null) {
205 prev.next = e.next;
206 } else {
207 tab[index] = e.next;
208 }
209 count--;
210 } else {
211 e.value = newValue;
212 }
213 return newValue;
214 }
215 }
216
217 V newValue = remappingFunction.apply(key, null);
218 if (newValue != null) {
219 addEntry(hash, key, newValue, index);
220 }
221
222 return newValue;
223 }
224
225 @Override
226 public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
227 Objects.requireNonNull(remappingFunction);
228
229 Entry<?,?> tab[] = table;
230 int hash = key.hashCode();
231 int index = (hash & 0x7FFFFFFF) % tab.length;
232 @SuppressWarnings("unchecked")
233 Entry<K,V> e = (Entry<K,V>)tab[index];
234 for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
235 if (e.hash == hash && e.key.equals(key)) {
236 V newValue = remappingFunction.apply(e.value, value);
237 if (newValue == null) {
238 modCount++;
239 if (prev != null) {
240 prev.next = e.next;
241 } else {
242 tab[index] = e.next;
243 }
244 count--;
245 } else {
246 e.value = newValue;
247 }
248 return newValue;
249 }
250 }
251
252 if (value != null) {
253 addEntry(hash, key, value, index);
254 }
255
256 return value;
257 }
十四、总结
1. Hashtable 是散列表的实现,处理散列冲突使用的是链表法,内部结构可以理解为「数组 + 链表」;
2. 默认初始化容量为 11,默认负载因子为 0.75;
3. 扩容后的新容量为旧容量的 2 倍 + 1;(int newCapacity = (oldCapacity << 1) + 1)
3. 线程安全,使用 synchronized 关键字,并发效率低;
4. 若无需保证线程安全,推荐使用 HashMap;若需要线程安全的高并发场景,推荐使用 ConcurrentHashMap。