Java 集合:(二十六) Hashtable 实现类

一、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。

原文地址:https://www.cnblogs.com/niujifei/p/14750675.html