Java集合框架09-Hashtable详解

相信大家对Hashtable已经有所了解了,Hashtable和HashMap,从存储结构和实现来讲基本上都是相同的。它和HashMap的最大的不同是它是线程安全的,另外它不允许key和value为null。Hashtable是个过时的集合类,不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换

顶部注释

This class implements a hash table, which maps keys to values. Any non-null object can be used as a key or as a value.

 这个类是哈希表的实现,key与value键值对的映射集。任何非null的对象可以用作key或者vlaue。

To successfully store and retrieve objects from a hashtable, the objects used as keys must implement the hashCode method and the equals method.

为了能在哈希表中成功地保存和取出对象,用作key的对象必须实现hashCode方法和equals方法。

An instance of Hashtable has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. Note that the hash table is open: in the case of a “hash collision”, a single bucket stores multiple entries, which must be searched sequentially. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. The initial capacity and load factor parameters are merely hints to the implementation. The exact details as to when and whether the rehash method is invoked are implementation-dependent.

一个Hashtable的实例有两个影响它行为的参数:初始化容量initial capacity 和负载因子load factor。容量是哈希表中桶的数量,初始化容量是哈希表被创建时的容量。哈希表是开放的,就哈希碰撞来说,一个桶存储数个必须被顺序查找的node。负载因子是哈希表在自动扩容之前可以多满的一个度量(哈希表几乎不会在满时才会扩容,加载因子越大,在扩容前哈希表可以存放的节点就越多)。初始化容量initial capacity 和负载因子load factor仅仅对实现有细微的暗示。何时扩容,是否扩容取决于具体的实现。

Generally, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the time cost to look up an entry (which is reflected in most Hashtable operations, including get and put).

一般来说,默认的加载因子0.75在哈希表和时间和空间花销上是一个很好的平衡。更高的加载因子减少了空间开销但增加了查找操作的时间开销(影响了大多的哈希表操作,包括get和put操作)。

The initial capacity controls a tradeoff between wasted space and the need for rehash operations, which are time-consuming. No rehash operations will ever occur if the initial capacity is greater than the maximum number of entries the Hashtable will contain divided by its load factor. However, setting the initial capacity too high can waste space.

初始化容量initial capacity在空间开销和扩容操作的时间开销之间控制平衡。如果initial capacity大于哈希表含有的node的数量/load factor,哈希表就不会扩容。然而,把initial capacity设置地太高就增大空间开销。

If many entries are to be made into a Hashtable, creating it with a sufficiently large capacity may allow the entries to be inserted more efficiently than letting it perform automatic rehashing as needed to grow the table.

如果提前知道hashtable将要存放许多node,创建hashtable时将initial capacity适当地设置地高些会使增加元素变得更有效率,否则容量不够大将导致频繁的扩容。

This example creates a hashtable of numbers. It uses the names of the numbers as keys: 
Hashtable<String, Integer> numbers= new Hashtable<String, Integer>(); 
numbers.put(“one”, 1); 
numbers.put(“two”, 2); 
numbers.put(“three”, 3);} 
To retrieve a number, use the following code: 
Integer n = numbers.get(“two”); 
if (n != null) { 
System.out.println(“two = ” + n); 
}}

这个例子演示了如何创建hashtable,存放元素,取出元素。

The iterators returned by the iterator method of the collections returned by all of this class’s “collection view methods” are fail-fast: if the Hashtable is structurally modified at any time after the iterator is created, in any way except through the iterator’s own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future. The Enumerations returned by Hashtable’s keys and elements methods are not fail-fast.

iterator方法返回的迭代器是fail-fast的。如果在迭代器被创建后hashtable被结构型地修改了,除了迭代器自己的remove方法,迭代器会抛出一个ConcurrentModificationException异常。因此,面对在并发的修改,迭代器干脆利落的失败,而不是冒险的继续。哈希表的key和元素集合返回的Enumerations不是fail-fast的。

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

迭代器的fail-fast机制并不能得到保证,它不能够保证一定出现该错误。一般来说,fail-fast会尽最大努力抛出ConcurrentModificationException异常。因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅用于检测 bug。

As of the Java 2 platform v1.2, this class was retrofitted to implement the Map interface, making it a member of the collection。

自从Java2开始,Hashtable继承Map接口,成为了容器中的一员。

Java Collections Framework. Unlike the new collection implementations, Hashtable is synchronized. If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use java.util.concurrent.ConcurrentHashMap in place of Hashtable.

和新的集合实现不同,Hashtable是线程安全的。如果不需要线程安全的实现是不需要的,推荐使用HashMap代替Hashtable。如果需要线程安全的实现,推荐使用java.util.concurrent.ConcurrentHashMap代替Hashtable。

第1部分 Hashtable介绍

Hashtable 简介

HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射
Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
Hashtable 的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。此外,Hashtable中的映射不是有序的。

Hashtable 的实例有两个参数影响其性能:初始容量 和 加载因子。容量 是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。
通常,默认加载因子是 0.75, 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括 get 和 put 操作,都反映了这一点)。

第2部分 Hashtable数据结构

Hashtable的继承关系

java.lang.Object
   ↳     java.util.Dictionary<K, V>
         ↳     java.util.Hashtable<K, V>

public class Hashtable<K,V> extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable { }

(01) Hashtable继承于Dictionary类,实现了Map接口。Map是"key-value键值对"接口,Dictionary是声明了操作"键值对"函数接口的抽象类。 
(02) Hashtable是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, count, threshold, loadFactor,modCount。
  table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。
  count是Hashtable的大小,它是Hashtable保存的键值对的数量。 
  threshold是Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值="容量*加载因子"。
  loadFactor就是加载因子。 
  modCount是用来实现fail-fast机制的

第3部分 Hashtable源码解析

package java.util;
import java.io.*;

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {

    // Hashtable保存key-value的数组。
    // Hashtable是采用拉链法实现的,每一个Entry本质上是一个单向链表
    private transient Entry<?,?>[] table;

    // Hashtable中元素的实际数量
    private transient int count;

    // 阈值,用于判断是否需要调整Hashtable的容量(threshold = 容量*加载因子)
    private int threshold;

    // 加载因子
    private float loadFactor;

    // Hashtable被改变的次数
    private transient int modCount = 0;

    // 序列版本号
    private static final long serialVersionUID = 1421746759512286392L;

/**
 * 使用指定参数初始化容量和指定参数负载因子来构造一个空的hashtable.
 *
 * @param      initialCapacity   指定参数初始化容量
 * @param      loadFactor        指定参数负载因子
 * @exception  IllegalArgumentException  如果initialCapacity小于0或者负载因子为非正数。
 */
public Hashtable(int initialCapacity, float loadFactor) {
    //如果指定参数初始化容量小于0,抛出异常
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    //如果指定参数负载因子为非正数,抛出异常
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal Load: "+loadFactor);
    //初始化hashtable的loadFactor、table、threshold属性
    if (initialCapacity==0)
        initialCapacity = 1;
    this.loadFactor = loadFactor;
    table = new Entry<?,?>[initialCapacity];
    //如果initialCapacity * loadFactor超出了MAX_ARRAY_SIZE,就使用MAX_ARRAY_SIZE 作为threshold
    threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}

/**
 * 使用指定参数初始化容量和默认负载因子(0.75)来构造一个空的hashtable.
 *
 * @param     initialCapacity   指定参数初始化容量
 * @exception IllegalArgumentException 如果initialCapacity小于0
 */
public Hashtable(int initialCapacity) {
    this(initialCapacity, 0.75f);
}

/**
 * 使用默认初始化容量(11)和默认负载因子(0.75)来构造一个空的hashtable.
 * 
 * 这里可以看到,Hashtable默认初始化容量为16,而HashMap的默认初始化容量为11。
 */
public Hashtable() {
    this(11, 0.75f);
}

/**
 * 使用指定的键值对集合t来构造hashtable。
 *
 * @param t 指定的键值对集合
 * @throws NullPointerException 如果指定的键值对集合为null
 * @since   1.2
 */
public Hashtable(Map<? extends K, ? extends V> t) {
    //初始hashMap
    this(Math.max(2*t.size(), 11), 0.75f);
    //将t中的键值对插入到hashtable中
    putAll(t);
}

/**
 * 返回hashtable中key的个数
 *
 * @return  返回hashtable中key的个数
 */
public synchronized int size() {
    return count;
}

/**
 * 判断hashtable中是否有键值对映射.
 *
 * @return  hashtable中没有键值对映射,返回true,否则返回false
 */
public synchronized boolean isEmpty() {
    return count == 0;
}

/**
 * 返回hashtable中key的枚举.
 *
 * @return  返回hashtable中key的枚举.
 * @see     Enumeration
 * @see     #elements()
 * @see     #keySet()
 * @see     Map
 */
public synchronized Enumeration<K> keys() {
    return this.<K>getEnumeration(KEYS);
}

/**
 * 返回hashtable中value的枚举.
 *
 * @return  返回hashtable中value的枚举
 * @see     java.util.Enumeration
 * @see     #keys()
 * @see     #values()
 * @see     Map
 */
public synchronized Enumeration<V> elements() {
    return this.<V>getEnumeration(VALUES);
}

/**
 * 返回hashtable中是否含有指定参数value。
 * 这个方法比containsKey方法代价更昂贵
 *
 * @param      value   指定参数value
 * @return     hashtable中含有指定参数value
 * @exception  NullPointerException  value为null
 */
public synchronized boolean contains(Object value) {
    if (value == null) {
        throw new NullPointerException();
    }

    Entry<?,?> tab[] = table;
    //需要遍历hashtable的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;
}

/**
 * 返回hashtable中是否含有指定参数value。
 * 这个方法比containsKey方法代价更昂贵
 *
 * @param      value   指定参数value
 * @return     hashtable中含有指定参数value
 * @exception  NullPointerException  value为null
 * @since 1.2
 */
public boolean containsValue(Object value) {
    return contains(value);
}

/**
 * Tests if the specified object is a key in this hashtable.
 * 判断hashtable是否含有指定参数key
 * @param   key   指定参数key
 * @return  hashtable含有指定参数key,返回true,否则返回false
 * @throws  NullPointerException  如果key为null
 * @see     #contains(Object)
 */
public synchronized boolean containsKey(Object key) {
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    //因为不需要从头开始遍历table,所以代价比containsValue要小
    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;
}

/**
 * 返回指定参数key映射的value,如果没有对应映射,返回null
 *
 * @param key 指定参数
 * @return 返回指定参数key映射的value,如果没有对应映射,返回null
 * @throws NullPointerException 如果指定参数key为null
 * @see     #put(Object, Object)
 */
@SuppressWarnings("unchecked")
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;
}

/**
 * 分派给arrays的最大容量
 * 为什么要减去8呢?
 * 因为某些VM会在数组中保留一些头字,尝试分配这个最大存储容量,可能会导致array容量大于VM的limit,最终导致OutOfMemoryError。
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
 * 增加hashtable的容量,为了更有效地存放和找到它的entry。
 * 当键值对的数量超过了临界值(capacity*load factor)这个方法自动调用
 * 长度变为原来的2倍+1
 * 
 */
@SuppressWarnings("unchecked")
protected void rehash() {
    //记录旧容量
    int oldCapacity = table.length;
    //记录旧桶的数组
    Entry<?,?>[] oldMap = table;

    // overflow-conscious code
    //新的容量为旧的容量的2倍+1
    int newCapacity = (oldCapacity << 1) + 1;
    //如果新的容量大于容量的最大值MAX_ARRAY_SIZE 
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
        //如果旧容量为MAX_ARRAY_SIZE,容量不变,中断方法的执行
        if (oldCapacity == MAX_ARRAY_SIZE)
            // Keep running with MAX_ARRAY_SIZE buckets
            return;
        //如果旧容量不为MAX_ARRAY_SIZE,新容量变为MAX_ARRAY_SIZE
        newCapacity = MAX_ARRAY_SIZE;
    }
    //创建新的数组,容量为新容量
    Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
    //结构性修改次数+1
    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;
            e.next = (Entry<K,V>)newMap[index];
            newMap[index] = e;
        }
    }
}

/**
 * 根据指参数向table中添加entry
 * put方法会使用此方法
 */
private void addEntry(int hash, K key, V value, int index) {
    //结构性修改次数+1
    modCount++;
    //记录现在的table
    Entry<?,?> tab[] = table;
    //如果现在的entry数量大于临界值
    if (count >= threshold) {
        // 扩容
        rehash();
        //记录新的table
        tab = table;
        //重新计算key的hash
        hash = key.hashCode();
        //重新计算index
        index = (hash & 0x7FFFFFFF) % tab.length;
    }

    // 创建一个新的entry
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>) tab[index];
    //将entry添加到table中
    tab[index] = new Entry<>(hash, key, value, e);
    //table大小+1
    count++;
}

/**
 * 添加指定键值对到hashtable中
 * 被添加的键值对中的key和value都不能为null
 *
 * value可以通过get方法被取出。
 *
 * @param      key     the hashtable key
 * @param      value   the value
 * @return     如果hashtable中已经存在key,则返回原来的value
 * @exception  NullPointerException  如果key或者value为null
 * @see     Object#equals(Object)
 * @see     #get(Object)
 */
public synchronized V put(K key, V value) {
    // 确认value不为null
    if (value == null) {
        throw new NullPointerException();
    }

    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    //找到key在table中的索引
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    //获取key所在索引的entry
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    //遍历entry,判断key是否已经存在
    for(; entry != null ; entry = entry.next) {
        //如果key已经存在
        if ((entry.hash == hash) && entry.key.equals(key)) {
            //保存旧的value
            V old = entry.value;
            //替换value
            entry.value = value;
            //返回旧的value
            return old;
        }
    }
    //如果key在hashtable不是已经存在,就直接将键值对添加到table中,返回null
    addEntry(hash, key, value, index);
    return null;
}

/**
 * 删除hashtable中参数key映射的键值对。如果参数key在hashtable不存在,方法不做任何操作。
 *
 * @param   key   参数key
 * @return  参数key映射的value,如果不存在对应的映射,返回null。
 * @throws  NullPointerException  如果key为null
 */
public synchronized V remove(Object key) {
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    //计算key在hashtable中的索引
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>)tab[index];
    //遍历entry,如果entry中存在key为参数key的键值对,就删除键值对,并返回键值对的value
    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;
        }
    }
    //如果不存在key为参数key的键值对,返回value
    return null;
}

/**
 * 将指定t中所有的键值对复制到hashtable中。
 * 如果t中的键值对的key在hashtable中已经存在,就替换。
 *
 * @param t 指定参数t
 * @throws NullPointerException 如果指定参数t为null
 * @since 1.2
 */
public synchronized void putAll(Map<? extends K, ? extends V> t) {
    //遍历参数t中所有的键值对,将其复制到hashtable中
    for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
        put(e.getKey(), e.getValue());
}

/**
 * 清空hashtable中所有的键值对
 */
public synchronized void clear() {
    Entry<?,?> tab[] = table;
    modCount++;
    //遍历hashtable中所有的entry,将其置为null,
    for (int index = tab.length; --index >= 0; )
        tab[index] = null;
    //修改hashtable大小为0
    count = 0;
}

/**
 * 创建一个hashtable的浅拷贝。
 * hashtable所有的结构都被拷贝,但键值对没有拷贝。
 * 这是个相对来说代价比较大的操作。
 *
 * @return  一个hashtable的浅拷贝
 */
public synchronized Object clone() {
    try {
        //调用父类的clone方法,浅拷贝一个HashTable对象t
        Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
        //给table属性赋值
        t.table = new Entry<?,?>[table.length];
        //遍历原散列数组,单独地拷贝并生成每个桶的链表。
        for (int i = table.length ; i-- > 0 ; ) {
            t.table[i] = (table[i] != null)
                ? (Entry<?,?>) table[i].clone() : null;
        }
        //给keySet属性赋值
        t.keySet = null;
        //给entrySet属性赋值
        t.entrySet = null;
        //给values 属性赋值
        t.values = null;
        //给modCount 属性赋值
        t.modCount = 0;
        //返回浅拷贝
        return t;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

/**
 * 返回hashtable的字符串表现形式。
 *
 * @return  hashtable的字符串表现形式。
 */
public synchronized String toString() {
    int max = size() - 1;
    //如果hashtable大小为0,返回"{}"
    if (max == -1)
        return "{}";

    //使用StringBuilder 
    StringBuilder sb = new StringBuilder();
    //获取entry迭代器
    Iterator<Map.Entry<K,V>> it = entrySet().iterator();

    sb.append('{');
    //遍历hashtable
    for (int i = 0; ; i++) {
        Map.Entry<K,V> e = it.next();
        K key = e.getKey();
        V value = e.getValue();
        //组装hashtable的字符串表示
        sb.append(key   == this ? "(this Map)" : key.toString());
        sb.append('=');
        sb.append(value == this ? "(this Map)" : value.toString());

        //i=max=size() - 1,到了最后一个entry
        if (i == max)
            return sb.append('}').toString();
        sb.append(", ");
    }
}

/**
 * 获取Hashtable的枚举类对象
 * 
 * @param type 0——返回key的枚举类对象/迭代器;1——返回values的枚举类对象/迭代器,2——返回entry的枚举类对象/迭代器
 */
private <T> Enumeration<T> getEnumeration(int type) {
    if (count == 0) {
        return Collections.emptyEnumeration();
    } else {
        //false,意味着不是迭代器,如果为true,说明返回的是迭代器
        return new Enumerator<>(type, false);
    }
}

// Enumerations/Iterations的类型。
private static final int KEYS = 0;
private static final int VALUES = 1;
private static final int ENTRIES = 2;

/**
 * 获取Hashtable的迭代器
 * 
 * @param type 0——返回key的枚举类对象/迭代器;1——返回values的枚举类对象/迭代器,2——返回entry的枚举类对象/迭代器
 */
private <T> Iterator<T> getIterator(int type) {
    if (count == 0) {
        return Collections.emptyIterator();
    } else {
        //true,意味着是迭代器,如果为false,说明返回的不是迭代器
        return new Enumerator<>(type, true);
    }
}

// Enumerations/Iterations的类型。
private static final int KEYS = 0;
private static final int VALUES = 1;
private static final int ENTRIES = 2;

    // Hashtable的“key的集合”。它是一个Set,意味着没有重复元素
    private transient volatile Set<K> keySet = null;
    // Hashtable的“key-value的集合”。它是一个Set,意味着没有重复元素
    private transient volatile Set<Map.Entry<K,V>> entrySet = null;
    // Hashtable的“key-value的集合”。它是一个Collection,意味着可以有重复元素
    private transient volatile Collection<V> values = null;

/**
 * 返回hashtable中包含的所有key的set视图。
 * 
 * set视图是由hashtable返回的,所以改变hashtable会改变set,反之亦然。
 * 
 * 如果迭代器在遍历set时,hashtable被修改(除了该迭代器自己的remove方法修改),迭代器的结果是不确定的。
 * 
 * set支持元素的删除,删除操作会删除hashtable中对应的键值对,删除操作包括Iterator.remove、Set.remove、removeAll、retainAll、clear。不支持add或者addAll方法。
 *
 * @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();
    }
}

/**
 * 返回hashtable中包含的所有entry的set视图。
 * 
 * set视图是由hashtable返回的,所以改变hashtable会改变set,反之亦然。
 * 
 * 如果迭代器在遍历set时,hashtable被修改(除了该迭代器自己的remove方法修改),迭代器的结果是不确定的。
 * 
 * set支持元素的删除,删除操作会删除hashtable中对应的键值对,删除操作包括Iterator.remove、Set.remove、removeAll、retainAll、clear。不支持add或者addAll方法。
 *
 * @since 1.2
 */
public Set<Map.Entry<K,V>> entrySet() {
    if (entrySet==null)
        entrySet = Collections.synchronizedSet(new EntrySet(), this);
    return entrySet;
}

private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
    public Iterator<Map.Entry<K,V>> iterator() {
        return getIterator(ENTRIES);
    }

    public boolean add(Map.Entry<K,V> o) {
        return super.add(o);
    }

    public boolean contains(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
        Object key = entry.getKey();
        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.equals(entry))
                return true;
        return false;
    }

    public boolean remove(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
        Object key = entry.getKey();
        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.equals(entry)) {
                modCount++;
                if (prev != null)
                    prev.next = e.next;
                else
                    tab[index] = e.next;

                count--;
                e.value = null;
                return true;
            }
        }
        return false;
    }

    public int size() {
        return count;
    }

    public void clear() {
        Hashtable.this.clear();
    }
}

/**
 * 返回hashtable中包含的所有value的collection视图。
 * 
 * collection视图是由hashtable返回的,所以改变hashtable会改变collection,反之亦然。
 * 
 * 如果迭代器在遍历collection时,hashtable被修改(除了该迭代器自己的remove方法修改),迭代器的结果是不确定的。
 * 
 * collection支持元素的删除,删除操作会删除hashtable中对应的键值对,删除操作包括Iterator.remove、Collection.remove、removeAll、retainAll、clear。不支持add或者addAll方法。
 *
 * @since 1.2
 */
public Collection<V> values() {
    if (values==null)
        values = Collections.synchronizedCollection(new ValueCollection(),
                                                    this);
    return values;
}

private class ValueCollection extends AbstractCollection<V> {
    public Iterator<V> iterator() {
        return getIterator(VALUES);
    }
    public int size() {
        return count;
    }
    public boolean contains(Object o) {
        return containsValue(o);
    }
    public void clear() {
        Hashtable.this.clear();
    }
}

/**
 * 由Map接口的定义比较指定参数和hashtable是否相等。
 *
 * @param  o 指定参数
 * @return 如果相等,返回true
 * @see Map#equals(Object)
 * @since 1.2
 */
public synchronized boolean equals(Object o) {
    //如果参数就是hashtable,返回true
    if (o == this)
        return true;

    //如果参数o不是map,返回false
    if (!(o instanceof Map))
        return false;
    Map<?,?> t = (Map<?,?>) o;
    //如果大小不同,返回false
    if (t.size() != size())
        return false;

    try {
        //遍历hashtable,如果所有的key和value在参数o中有一条没有对应,说明不等,返回false。
        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;
    }
    //所有的key和value都能对应上,返回true
    return true;
}

/**
 * 根据Map接口的定义返回哈希值
 * 
 * @see Map#hashCode()
 * @since 1.2
 */
public synchronized int hashCode() {
    /*
     * 负的负载因子表明hashcode的计算还在进行中。
     */
    int h = 0;
    //当hashtable大小为0或者负载因子小于0
    if (count == 0 || loadFactor < 0)
        //返回0
        return h;  // Returns zero

    //将loadFactor变为负数
    loadFactor = -loadFactor;  // Mark hashCode computation in progress
    Entry<?,?>[] tab = table;
    //遍历hashtable中所有的entry
    for (Entry<?,?> entry : tab) {
        //如果entry不为null
        while (entry != null) {
            //hashcode加entry的hashcode
            h += entry.hashCode();
            //准备entry的下个entry
            entry = entry.next;
        }
    }
    //将loadFactor变为正数
    loadFactor = -loadFactor;  // Mark hashCode computation complete
    //返回hashcode
    return h;
}

/**
 * 返回指定参数key映射的value,如果没有对应映射,返回默认值defaultValue
 *
 * @param key 指定参数key
 * @param defaultValue 默认值
 * @return 返回指定参数key映射的value,如果没有对应映射,返回默认值
 * @throws NullPointerException 如果指定参数key为null
 * @see     #put(Object, Object)
 * @see     #get()
 */
 @Override
public synchronized V getOrDefault(Object key, V defaultValue) {
    V result = get(key);
    return (null == result) ? defaultValue : result;
}

/**
 * 在hashtable中插入参数key和value组成的键值对,如果key已经存在,返回旧value,如果旧value为null,则用参数value替换旧value
 *
 * @return 如果key在hashtable中存在,返回旧value
 */
@Override
public synchronized V putIfAbsent(K key, V value) {
    //判断value是否为null,如果为null,抛出NullPointerException
    Objects.requireNonNull(value);

    // 确认key是不是已经才hashtable中存在
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    //获取key在hashtable中的索引
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    //根据key在hashtable中的索引获取对应entry
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    //遍历entry中的所有键值对,如果key已经存在,返回旧value,如果旧value为null,则用参数value替换旧value
    for (; entry != null; entry = entry.next) {
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            if (old == null) {
                entry.value = value;
            }
            return old;
        }
    }
    //如果,key在entry中不存在,添加entry,返回null
    addEntry(hash, key, value, index);
    return null;
}

/**
 * 在hashtable中删除key和value都和参数key和参数value匹配的键值对
 *
 * @return 如果删除成功,返回true
 */
@Override
public synchronized boolean remove(Object key, Object value) {
    //如果value为null,抛出空指针异常
    Objects.requireNonNull(value);

    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    //计算key在hashtable中的索引
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    //根据key在hashtable中的索引获取对应entry
    Entry<K,V> e = (Entry<K,V>)tab[index];
    //遍历entry,如果entry中存在和参数value和参数key都存在的键值对,则删除这个键值对,并返回true
    for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
        if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
            modCount++;
            if (prev != null) {
                prev.next = e.next;
            } else {
                tab[index] = e.next;
            }
            count--;
            e.value = null;
            return true;
        }
    }
    //如果entry中不存在和参数value和参数key都存在的键值对,返回false
    return false;
}

/**
 * 在hashtable中查找key和value都和参数key和参数oldValue都匹配的键值对,如果找到,将键值对的value替换为参数newValue
 *
 * @return 如果替换成功,返回true
 */
@Override
public synchronized boolean replace(K key, V oldValue, V newValue) {
    //如果oldValue或者newValue为null,抛出空指针异常
    Objects.requireNonNull(oldValue);
    Objects.requireNonNull(newValue);
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    //计算key在hashtable中的索引
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    //根据key在hashtable中的索引获取entry
    Entry<K,V> e = (Entry<K,V>)tab[index];
    //遍历entry,如果key和value都和参数key和参数oldValue都匹配的键值对,如果找到,将键值对的value替换为参数newValue,返回true。如果都不匹配,返回false
    for (; e != null; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            if (e.value.equals(oldValue)) {
                e.value = newValue;
                return true;
            } else {
                return false;
            }
        }
    }
    //如果都不匹配,返回false
    return false;
}

/**
 * 在hashtable中查找key和参数key匹配的键值对,如果找到,将键值对的value替换为参数value
 *
 * @return 如果替换成功,返回键值对的旧value
 */
@Override
public synchronized V replace(K key, V value) {
    //如果value为null,抛出空指针异常
    Objects.requireNonNull(value);
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    //计算key在hashtable中的索引
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    //根据key在hashtable中的索引获取entry
    Entry<K,V> e = (Entry<K,V>)tab[index];
    //遍历entry,如果存在key和参数key匹配的键值对,将键值对的value替换为参数value,返回true。如果都不匹配,返回null
    for (; e != null; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            V oldValue = e.value;
            e.value = value;
            return oldValue;
        }
    }
    return null;
}

/**
 * 序列化hashtable到ObjectOutputStream中
 * 将hashtable的总容量table.length、实际容量count、键值对映射写入到ObjectOutputStream中。键值对映射序列化时是无序的。
 */
private void writeObject(java.io.ObjectOutputStream s)
        throws IOException {
    Entry<Object, Object> entryStack = null;

    synchronized (this) {
        // 写入临界值和负载因子
        s.defaultWriteObject();

        // 写入总容量和实际大小
        s.writeInt(table.length);
        s.writeInt(count);

        // 
        for (int index = 0; index < table.length; index++) {
            Entry<?,?> entry = table[index];

            while (entry != null) {
                entryStack =
                    new Entry<>(0, entry.key, entry.value, entryStack);
                entry = entry.next;
            }
        }
    }

    // 写入hashtable键值对到ObjectOutputStream中
    while (entryStack != null) {
        s.writeObject(entryStack.key);
        s.writeObject(entryStack.value);
        entryStack = entryStack.next;
    }
}

/**
 * 反序列化
 */
private void readObject(java.io.ObjectInputStream s)
     throws IOException, ClassNotFoundException
{
    // 读出临界值和负载因子
    s.defaultReadObject();

    // 验证负载因子,忽略临界值,因为它会被重新计算
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new StreamCorruptedException("Illegal Load: " + loadFactor);

    // 读出hashtable总容量和实际大小
    int origlength = s.readInt();
    int elements = s.readInt();

    // 验证实际大小
    if (elements < 0)
        throw new StreamCorruptedException("Illegal # of Elements: " + elements);

    // 重新计算总容量,使其大于(实际大小/负载因子)+1
    origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);

    // Compute new length with a bit of room 5% + 3 to grow but
    // no larger than the clamped original length.  Make the length
    // odd if it's large enough, this helps distribute the entries.
    // Guard against the length ending up zero, that's not valid.
    int length = (int)((elements + elements / 20) / loadFactor) + 3;
    if (length > elements && (length & 1) == 0)
        length--;
    length = Math.min(length, origlength);
    table = new Entry<?,?>[length];
    threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
    count = 0;

    // 读出所有的key-value键值对,并将其添加到table中
    for (; elements > 0; elements--) {
        @SuppressWarnings("unchecked")
            K key = (K)s.readObject();
        @SuppressWarnings("unchecked")
            V value = (V)s.readObject();
        // sync is eliminated for performance
        reconstitutionPut(table, key, value);
    }
}

/**
 * 此方法被readObject方法使用。
 * 提供该方法是因为put方法是可重写的,不应该被readObject调用。
 *
 * 该方法和put方法在以下几个方面不同:
 * 从hashtable容量被初始化开始,不扩容。
 * modCount不增长
 * 不同步,因为我们在创建一个新的实例
 * 不需要返回值
 */
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
    throws StreamCorruptedException
{
    if (value == null) {
        throw new java.io.StreamCorruptedException();
    }
    // Makes sure the key is not already in the hashtable.
    // This should not happen in deserialized version.
    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)) {
            throw new java.io.StreamCorruptedException();
        }
    }
    // 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的Entry节点,它本质上是一个单向链表。
    // 也因此,我们才能推断出Hashtable是由拉链法实现的散列表
    private static class Entry<K,V> implements Map.Entry<K,V> {
        // 哈希值
        int hash;
        K key;
        V value;
        // 指向的下一个Entry,即链表的下一个节点
        Entry<K,V> next;

        // 构造函数
        protected Entry(int hash, K key, V value, Entry<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        protected Object clone() {
            return new Entry<K,V>(hash, key, value,
                  (next==null ? null : (Entry<K,V>) next.clone()));
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        // 设置value。若value是null,则抛出异常。
        public V setValue(V value) {
            if (value == null)
                throw new NullPointerException();

            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        // 覆盖equals()方法,判断两个Entry是否相等。
        // 若两个Entry的key和value都相等,则认为它们相等。
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;

            return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
               (value==null ? e.getValue()==null : value.equals(e.getValue()));
        }

        public int hashCode() {
            return hash ^ Objects.hashCode(value);
        }

        public String toString() {
            return key.toString()+"="+value.toString();
        }
    }

    private static final int KEYS = 0;
    private static final int VALUES = 1;
    private static final int ENTRIES = 2;

    // Enumerator的作用是提供了“通过elements()遍历Hashtable的接口” 和 “通过entrySet()遍历Hashtable的接口”。因为,它同时实现了 “Enumerator接口”和“Iterator接口”。
    private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
        // 指向Hashtable的table
        Entry<?,?>[] table = Hashtable.this.table;
        // Hashtable的总的大小
        int index = table.length;
        Entry<?,?> entry;
        Entry<?,?> lastReturned;
        int type;

        // Enumerator是 “迭代器(Iterator)” 还是 “枚举类(Enumeration)”的标志
        // iterator为true,表示它是迭代器;否则,是枚举类。
        boolean iterator;

        // 在将Enumerator当作迭代器使用时会用到,用来实现fail-fast机制。
        protected int expectedModCount = modCount;

        Enumerator(int type, boolean iterator) {
            this.type = type;
            this.iterator = iterator;
        }

        // 从遍历table的数组的末尾向前查找,直到找到不为null的Entry。
        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;
        }

        // 获取下一个元素
        // 注意:从hasMoreElements() 和nextElement() 可以看出“Hashtable的elements()遍历方式”
        // 首先,从后向前的遍历table数组。table数组的每个节点都是一个单向链表(Entry)。
        // 然后,依次向后遍历单向链表Entry。
        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的判断是否存在下一个元素
        // 实际上,它是调用的hasMoreElements()
        public boolean hasNext() {
            return hasMoreElements();
        }

        // 迭代器获取下一个元素
        // 实际上,它是调用的nextElement()
        public T next() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            return nextElement();
        }

        // 迭代器的remove()接口。
        // 首先,它在table数组中找出要删除元素所在的Entry,
        // 然后,删除单向链表Entry中的元素。
        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();
            }
        }
}

总结

Hashtable与HashMap不同点

关于Hashtable的源码就看到这,从代码中我们可以总结出Hashtable与HashMap的几个不同点:

不同点HashMapHashtable
数据结构数组+链表+红黑树数组+链表
继承的类不同继承AbstractMap继承Dictionary
是否线程安全
性能高低
默认初始化容量1611
扩容方式不同原始容量×2原始容量×2+1
底层数组的容量为2的整数次幂要求一定为2的整数次幂不要求
确认key在数组中的索引的方法不同i=(n-1)&hash;index=(hash&0x7FFFFFFF)%tab.length;
遍历方式Iterator(迭代器)Iterator(迭代器)和Enumeration(枚举器)
Iterator遍历数组顺序索引从小到大索引从大到小

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值