Hashtable

来源:互联网 发布:js轮播图代码 编辑:程序博客网 时间:2024/05/23 10:40

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
原创粉丝点击