• 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。

  • 相关阅读:
    webservice+linq
    Javascript之四
    HTML、CSS、JS课后习作——痛风诊断APP源代码
    HTML、CSS、JS课后习作——痛风诊断APP
    Javascript之三
    Javascript之二
    Javascript之一
    CSS(之五)
    CSS(之四)
    CSS(之三)
  • 原文地址:https://www.cnblogs.com/niujifei/p/14750675.html
Copyright © 2020-2023  润新知