Hashtable

Hashtable

Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable{
}

这个类实现了一个键值哈希表,其中Key值对象的类要实现hashCode和equals方法。

哈希表有两个重要的参数:初始容量值(initial capacity )和装填因子(load factor),容量值是哈希表中桶(buckets)的数量,容量值默认值为11,装填因子默认值为0.75。哈希表解决哈希碰撞的方法:每个桶中存储多个实体,这些实体需要顺序搜索。当哈希表中的实体数量与容量的比值超过装填因子时,哈希表将自动扩容。

注意:哈希表的容量值一般为质数

装填因子设置为0.75是对时间和空间消费的折中选择。设置太高,则增加时间花费;设置太低,则浪费空间。当哈希表扩容时,需要对原有哈希表中的实体进行重新哈希,会有时间消耗,如果初始容量设置 capacity=maxnum/loadfator ,则可以避免哈希表扩容。初始容量不宜太大,否则会浪费空间,但是设置足够大总比不断扩容重哈希要好很多。

The iterators returned by the iterator method of the collections returned by all of this class’s “collection view methods” are fail-fast.

fail-fast机制:当Iterator操作时,集合中的元素发生结构性的改变(不是由于Iterator.remove方法引起的),Iterator就会抛出ConcurrentModificationException。在并发模式下,iterator会快速并且干净的失败,而不是在后面某个操作时具有任意的、具有冒险行为的、不确定的。Hashtable的枚举方法所返回的枚举对象不是fail-fast的。

fail-fast的行为不能得到保证,一般来说,在面对不同步的并发修改时是不可能做出任何保证的。itertaor的fail-fast应当只用于检测错误,而不是利用它使得程序的迭代行为运行正确。

Hashtable是同步的、线程安全的,如果程序不需要同步,可以使用HashMap代替。如果需要一个高并发的线程安全的类,则可以使用ConcurrentHashMap。

Hashtable的方法是加了synchronized关键词。

Hashtable成员变量

/**
* The hash table data.
* 使用Entry数组存储数据
*/
private transient Entry<?,?>[] table;

/**
* The total number of entries in the hash table.
*/
private transient int count;

/**
* The table is rehashed when its size exceeds this threshold.  (The
* value of this field is (int)(capacity * loadFactor).)
* 
*重新hash的阈值
* @serial
*/
private int threshold;

/**
* The load factor for the hashtable.
*装填因子
* @serial
*/
private float loadFactor;

/**
* The number of times this Hashtable has been structurally modified
* Structural modifications are those that change the number of entries in
* the Hashtable or otherwise modify its internal structure (e.g.,
* rehash).  This field is used to make iterators on Collection-views of
* the Hashtable fail-fast.  (See ConcurrentModificationException).
* 
* Hashtable结构改变的次数,用于fail-fast机制
*/
private transient int modCount=0;

/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
* 
* 分配数组的最大值
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

Hashtable方法

/**
* 构造函数,默认capacity=11,loadFactor=0.75;
* threshold最大值为Integer.MAX_VALUE-8+1;
*/
public Hashtable(int initialCapacity, float loadFactor);
public Hashtable(int initialCapacity);
public Hashtable();

/**
* 注意是两倍的t.size()
*/
public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }

public synchronized int size();
public synchronized boolean isEmpty();

public synchronized Enumeration<K> keys() {
        return this.<K>getEnumeration(KEYS);
    }

public synchronized Enumeration<V> elements() {
        return this.<V>getEnumeration(VALUES);

/**
* 对每个桶中的链表进行遍历,所以要用到两个for循环
* 判断是否存在value值的键值对,比查找是否存在key值的键值对花费要高很多,因为key可以hash查找,而value只能挨个查找对比。
*/
public synchronized boolean contains(Object value) {
        if (value == null) {
            throw new NullPointerException();
        }

        Entry<?,?> tab[] = 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;
    }

public boolean containsValue(Object value);

public synchronized boolean containsKey(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 true;
            }
        }
        return false;
    }

 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;
    }

/**
* 以接近两倍的原有容量扩展。
* newCapacity = oldCapacity << 1 + 1; 两倍加1,也就是尽可能避免选择能被2整除的数。最多也就是MAX_ARRAY_SIZE.
* 在桶中链表插入的时候,插入在头部,头部也是存储有值的。
*/
protected void rehash() {
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 1;
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

        modCount++;
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;

        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;

                int index = (e.hash & 0x7FFFFFFF) % newCapacity;

                //看懂这一点,和C++中的指针不同,这里没有专门的头指针。
                e.next = (Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }


/**
*private函数,在后面几个方法中会调用。
*添加一个Entry,Entry类将在稍后介绍
*Entry成员变量包含有:hash,key,value,next。
*/
private void addEntry(int hash, K key, V value, int index) {
        modCount++;

        Entry<?,?> tab[] = table;
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();

            tab = table;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // 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中有key值所对应的键值对,则更新key所对应的valu,否则创建并添加一个新的Entry。
* key 和 value都不能为null。当key为null时,key.hashCode()抛异常,当value为null时,检查并抛异常。
*/
public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

/**
*注意删除时,要有个保存前一个结点的位置,所以有个prev变量。另外还要注意,删除的是数组中的元素还是数组内链表的元素。
*
*/
public synchronized V remove(Object key) {
        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.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;
            }
        }
        return null;
    }

 public synchronized void putAll;

/**
*清除的时候要对数组内的每个对象进行清除。
*/
 public synchronized void clear() {
        Entry<?,?> tab[] = table;
        modCount++;
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;
        count = 0;
    }

/**
*注意这里是深拷贝。
*请注意比浅拷贝和深拷贝的区别。
*/
public synchronized Object clone() {
        try {
            Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
            t.table = new Entry<?,?>[table.length];
            for (int i = table.length ; i-- > 0 ; ) {
                t.table[i] = (table[i] != null)
                    ? (Entry<?,?>) table[i].clone() : null;
            }
            t.keySet = null;
            t.entrySet = null;
            t.values = null;
            t.modCount = 0;
            return t;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

/**
* 注意String的连接操作尽量用StringBuilder.
*/
public synchronized String toString() {
        int max = size() - 1;
        if (max == -1)
            return "{}";

        StringBuilder sb = new StringBuilder();
        Iterator<Map.Entry<K,V>> it = entrySet().iterator();

        sb.append('{');
        for (int i = 0; ; i++) {
            Map.Entry<K,V> e = it.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key.toString());
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value.toString());

            if (i == max)
                return sb.append('}').toString();
            sb.append(", ");
        }
    }

/**
*type是用于区分返回的是key还是value的枚举
*/
 private <T> Enumeration<T> getEnumeration(int type) {
        if (count == 0) {
            return Collections.emptyEnumeration();
        } else {
            return new Enumerator<>(type, false);
        }
    }

    private <T> Iterator<T> getIterator(int type) {
        if (count == 0) {
            return Collections.emptyIterator();
        } else {
            return new Enumerator<>(type, true);
        }
    }
/**
* Each of these fields are initialized to contain an instance of the
* appropriate view the first time this view is requested.  The views are
* stateless, so there's no reason to create more than one of each.
* 有且只有一份,每次访问的时候返回的都是同一份。
*/
private transient volatile Set<K> keySet;
private transient volatile Set<Map.Entry<K,V>> entrySet;
private transient volatile Collection<V> values;

/**
     * Returns a {@link Set} view of the keys contained in this map.
     * The set is backed by the map, so changes to the map are
     * reflected in the set, and vice-versa.  If the map is modified
     * while an iteration over the set is in progress (except through
     * the iterator's own <tt>remove</tt> operation), the results of
     * the iteration are undefined.  The set supports element removal,
     * which removes the corresponding mapping from the map, via the
     * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
     * operations.  It does not support the <tt>add</tt> or <tt>addAll</tt>
     * operations.
     *
     *对于keySet、entrySet、values、iterator等删除、清空操作是会改变Hashtable中的数据,Hashtable中的数据改变时,这些视图中的数据也会改变。
     *这些视图对象都是现成同步的。
     * @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();
        }
    }
/**
* equals方法三步:1,判断是否是自己;2,判断是否同一类型;3,判断内容是否相等。
*/
public synchronized boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map))
            return false;
        Map<?,?> t = (Map<?,?>) o;
        if (t.size() != size())
            return false;

        try {
            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;
        }

        return true;
    }


public synchronized int hashCode() {
        /*
         * This code detects the recursion caused by computing the hash code
         * of a self-referential hash table and prevents the stack overflow
         * that would otherwise result.  This allows certain 1.1-era
         * applets with self-referential hash tables to work.  This code
         * abuses the loadFactor field to do double-duty as a hashCode
         * in progress flag, so as not to worsen the space performance.
         * A negative load factor indicates that hash code computation is
         * in progress.
         */
        int h = 0;
        if (count == 0 || loadFactor < 0)
            return h;  // Returns zero

        loadFactor = -loadFactor;  // Mark hashCode computation in progress
        Entry<?,?>[] tab = table;
        for (Entry<?,?> entry : tab) {
            while (entry != null) {
                h += entry.hashCode();
                entry = entry.next;
            }
        }

        loadFactor = -loadFactor;  // Mark hashCode computation complete

        return h;
    }

public synchronized V getOrDefault(Object key, V defaultValue) ;


/**
* 注意这里用到了modCount的值作为判断数据是否改动的依据。
*/
public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);     // explicit check required in case
                                            // table is empty.
        final int expectedModCount = modCount;

        Entry<?, ?>[] tab = table;
        for (Entry<?, ?> entry : tab) {
            while (entry != null) {
                action.accept((K)entry.key, (V)entry.value);
                entry = entry.next;

                if (expectedModCount != modCount) {
                    throw new ConcurrentModificationException();
                }
            }
        }
    }

 public synchronized void replaceAll;
 public synchronized V putIfAbsent;
 public synchronized boolean remove;

  ...太多了,不一一介绍了。
private static class Entry<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Entry<K,V> next;
        ...
 private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
        Entry<?,?>[] table = Hashtable.this.table;
        int index = table.length;
        Entry<?,?> entry;
        Entry<?,?> lastReturned;
        int type;

        /**
         * Indicates whether this Enumerator is serving as an Iterator
         * or an Enumeration.  (true -> Iterator).
         */
        boolean iterator;

        /**
         * The modCount value that the iterator believes that the backing
         * Hashtable should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        protected int expectedModCount = modCount;

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

        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;
        }

        @SuppressWarnings("unchecked")
        public T nextElement() {
            Entry<?,?> et = entry;
            int i = index;
            Entry<?,?>[] t = table;
            /* Use locals for faster loop iteration */
            while (et == null && i > 0) {
                et = t[--i];
            }
            entry = et;
            index = i;
            if (et != null) {
                Entry<?,?> e = lastReturned = entry;
                entry = e.next;
                return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
            }
            throw new NoSuchElementException("Hashtable Enumerator");
        }

        // Iterator methods
        public boolean hasNext() {
            return hasMoreElements();
        }

        public T next() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            return nextElement();
        }

        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();
            }
        }
    }

Iterator,Set…等删除操作会影响到原有数据。Iterator在遍历的过程中,原有数据不能发生结构性的改变,否则抛出ConcurrentModificationException,但可以用自己的remove方法使原有数据发生改变。Enumeration没有这种限制,即使原有数据在Enumeration遍历时发生结构性的改变,Enumeration也不会抛出异常,但是遍历结果不太理想,无法把控。Iterator和Enumeration遍历的时候都是从数组的最后开始往前遍历的。

hash值的计算公式

(hashCode() & 0x7FFFFFFF)%Capacity

为什么要&上0x7FFFFFFF,因为要保证计算出来的值为正整数(32位),所以最高位要默认为0,防止溢出,表示是正整数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值