源码分析—HashMap、HashSet、HashTable

来源:互联网 发布:st1000dm003 数据恢复 编辑:程序博客网 时间:2024/05/17 09:27

HashMap基本总结
(1)存储结构是采用数组+链表,元素类型为Entry,Entry为HashMap的内部静态类,持有四个属性(如下图所示)。注意的是Entry中定义的next同样也为Entry,用于把相同的hash值的数据存储为链表
这里写图片描述

这里写图片描述

(2)hash值相同并不代表key相同,在插入的时候,需要循环比较key值与hash值,如果找到了元素,则覆盖;否则会新插入一个节点,放在table表中,也就是链表的表头
(3)hashmap初始化有两个可选参数,initialCapacity(默认为1<<4)和loadFactor(默认为0.75)
(4)resizes时机为两个条件:
(size >= threshold) && (null != table[bucketIndex])扩容2倍
其中:
threshold = initialCapacity*loadFactor
size指的是当前的元素个数

HashMap put() 核心部分 操作分析
(1)计算hash值,并通过indexFor方法计算出在数组中的位置

int hash = hash(key);int i = indexFor(hash, table.length);

(2)查看当前key是否已经存在,如果存在则覆盖。注意:比较当前key是否存在分为两步:
hash值必须相等,key必须相等(直接==比较,这里是比较内存地址;或者equals是否相等)

这里需要强调的是,hashCode()方法的重写是为了确定在数组中元素的位置,也就是链表的头元素位置。而equals方法则是判断元素的key是否相同。
那么涉及到一个问题就是,如果元素的key无论是通过==或者equals方法比较是相同的,而hashCode却不相同。
这样就造成一个悖论,明明是同一个key值,为什么再次赋值却找到了其他位置。所以JDK要求我们重写equals()方法一定要重写hashCode()方法。同时有以下说明:

<1>两个对象equals,hashCode一定相同
<2>两个对象hashCode相同,对象不一定equals,这就会遇到hash冲突问题

for (Entry<K,V> e = table[i]; e != null; e = e.next) {    Object k;    if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {        V oldValue = e.value;        e.value = value;        e.recordAccess(this);        return oldValue;    }}

(3)如果没有这个元素,则插入新元素

modCount++;addEntry(hash, key, value, i);return null;

HashMap的resize()时机
在上述步骤(3)增加一个新节点时,addEntry(hash, key, value, i)会做容量是否超出的判断,也就是resize操作。

在这里可以清晰地看到(size >= threshold) && (null != table[bucketIndex])提交:也就是说当前元素的数目大于临界值时,并且目的存储的数组并不为空,才会对HashMap进行扩容,扩容是非常消耗性能的,需要重新计算原有数据在新存储的位置,全部做迁移。

void addEntry(int hash, K key, V value, int bucketIndex) {    if ((size >= threshold) && (null != table[bucketIndex])) {        resize(2 * table.length);        hash = (null != key) ? hash(key) : 0;        bucketIndex = indexFor(hash, table.length);    }    createEntry(hash, key, value, bucketIndex);}

HashSet
(1)HashSet时间上使用了HashMap作为存储的

public HashSet() {        map = new HashMap<>();    }

(2)key作为元素,value为一个占位object(没啥数据)

public boolean add(E e) {        return map.put(e, PRESENT)==null;    }private static final Object PRESENT = new Object();

(2)HashSet也是非线程安全的

Hashtable
Hashtable 结构同HashMap类似,主要区别在于通过使用synchronized关键字实现了线程安全。

HashMap源码清单(加了一部分注释)

package java.util;import java.io.*;public class HashMap<K,V>    extends AbstractMap<K,V>    implements Map<K,V>, Cloneable, Serializable{    //默认的初始容量大小,1左位移4位,即16    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16    //最大的容量,1左位移30位    static final int MAXIMUM_CAPACITY = 1 << 30;    //默认的加载因子,0.75    static final float DEFAULT_LOAD_FACTOR = 0.75f;    //构造一个空Entry数组    static final Entry<?,?>[] EMPTY_TABLE = {};    //保存元素的数组,初始化为空,transient关键字保证不进行序列化    transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;    //元素的个数    transient int size;    //rehash的临界值,等于capacity * load factor    int threshold;    //加载因子    final float loadFactor;    //数组结构改变的次数    transient int modCount;    /**     * The default threshold of map capacity above which alternative hashing is     * used for String keys. Alternative hashing reduces the incidence of     * collisions due to weak hash code calculation for String keys.     * <p/>     * This value may be overridden by defining the system property     * {@code jdk.map.althashing.threshold}. A property value of {@code 1}     * forces alternative hashing to be used at all times whereas     * {@code -1} value ensures that alternative hashing is never used.     */    static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;    /**     * holds values which can't be initialized until after VM is booted.     */    private static class Holder {        /**         * Table capacity above which to switch to use alternative hashing.         */        static final int ALTERNATIVE_HASHING_THRESHOLD;        static {            String altThreshold = java.security.AccessController.doPrivileged(                new sun.security.action.GetPropertyAction(                    "jdk.map.althashing.threshold"));            int threshold;            try {                threshold = (null != altThreshold)                        ? Integer.parseInt(altThreshold)                        : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;                // disable alternative hashing if -1                if (threshold == -1) {                    threshold = Integer.MAX_VALUE;                }                if (threshold < 0) {                    throw new IllegalArgumentException("value must be positive integer.");                }            } catch(IllegalArgumentException failed) {                throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed);            }            ALTERNATIVE_HASHING_THRESHOLD = threshold;        }    }    /**     * A randomizing value associated with this instance that is applied to     * hash code of keys to make hash collisions harder to find. If 0 then     * alternative hashing is disabled.     */    transient int hashSeed = 0;    //构造器    public HashMap(int initialCapacity, float loadFactor) {        //initialCapacity不能小于0        if (initialCapacity < 0)            throw new IllegalArgumentException("Illegal initial capacity: " +                                               initialCapacity);        //如果initialCapacity大于最大容量,那么等于最大容量        if (initialCapacity > MAXIMUM_CAPACITY)            initialCapacity = MAXIMUM_CAPACITY;        //验证加载因子的合法性        if (loadFactor <= 0 || Float.isNaN(loadFactor))            throw new IllegalArgumentException("Illegal load factor: " +                                               loadFactor);        //赋值loadFactor         this.loadFactor = loadFactor;        //赋值threshold        threshold = initialCapacity;        //空方法,方便子类扩展        init();    }    //构造器    public HashMap(int initialCapacity) {        this(initialCapacity, DEFAULT_LOAD_FACTOR);    }    //构造器    public HashMap() {        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);    }    //构造器    public HashMap(Map<? extends K, ? extends V> m) {        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);        inflateTable(threshold);        putAllForCreate(m);    }    private static int roundUpToPowerOf2(int number) {        // assert number >= 0 : "number must be non-negative";        int rounded = number >= MAXIMUM_CAPACITY                ? MAXIMUM_CAPACITY                : (rounded = Integer.highestOneBit(number)) != 0                    ? (Integer.bitCount(number) > 1) ? rounded << 1 : rounded                    : 1;        return rounded;    }    //扩展表空间    private void inflateTable(int toSize) {        // Find a power of 2 >= toSize        //必须是2的幂        int capacity = roundUpToPowerOf2(toSize);        //threshold重新赋值,取capacity * loadFactor和MAXIMUM_CAPACITY + 1的最小值        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);        //每次扩展表空间都是重新new数组,此操作非常消耗性能        table = new Entry[capacity];        //重新计算hashSeed值        initHashSeedAsNeeded(capacity);    }    void init() {    }    /**     * Initialize the hashing mask value. We defer initialization until we     * really need it.     */    final boolean initHashSeedAsNeeded(int capacity) {        boolean currentAltHashing = hashSeed != 0;        boolean useAltHashing = sun.misc.VM.isBooted() &&                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);        boolean switching = currentAltHashing ^ useAltHashing;        if (switching) {            hashSeed = useAltHashing                ? sun.misc.Hashing.randomHashSeed(this)                : 0;        }        return switching;    }    //重新计算hash值    final int hash(Object k) {        int h = hashSeed;        if (0 != h && k instanceof String) {            return sun.misc.Hashing.stringHash32((String) k);        }        h ^= k.hashCode();        // This function ensures that hashCodes that differ only by        // constant multiples at each bit position have a bounded        // number of collisions (approximately 8 at default load factor).        h ^= (h >>> 20) ^ (h >>> 12);        return h ^ (h >>> 7) ^ (h >>> 4);    }    //计算index,通过位运算保证返回的index<length    static int indexFor(int h, int length) {        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";        return h & (length-1);    }    public int size() {        return size;    }    public boolean isEmpty() {        return size == 0;    }    //根据key,找到value    public V get(Object key) {        //key为空的操作        if (key == null)            return getForNullKey();        //根据key获取entry        Entry<K,V> entry = getEntry(key);        //返回value        return null == entry ? null : entry.getValue();    }    private V getForNullKey() {        if (size == 0) {            return null;        }        for (Entry<K,V> e = table[0]; e != null; e = e.next) {            if (e.key == null)                return e.value;        }        return null;    }    public boolean containsKey(Object key) {        return getEntry(key) != null;    }    //根据key,找到Entry    final Entry<K,V> getEntry(Object key) {        //集合为空则直接返回null        if (size == 0) {            return null;        }        //计算hash值        int hash = (key == null) ? 0 : hash(key);        //根据index找到第一个Entry,通过判断hash值与key值相等,循环next,找到即返回,注意:indexFor方法计算出数组的位置,不同的key有可能计算出同一个index值        for (Entry<K,V> e = table[indexFor(hash, table.length)];             e != null;             e = e.next) {            Object k;            //命中的条件为hash 值相等或者key值相等            if (e.hash == hash &&                ((k = e.key) == key || (key != null && key.equals(k))))                return e;        }        //找不到,返回null        return null;    }    //插入元素    public V put(K key, V value) {        //如果数组是空,则在第一次插入,初始化表空间        if (table == EMPTY_TABLE) {            inflateTable(threshold);        }        //key为null的处理        if (key == null)            return putForNullKey(value);        //计算hash值        int hash = hash(key);        //计算index值        int i = indexFor(hash, table.length);        //是否有相同key已经存在,如果有就覆盖值        for (Entry<K,V> e = table[i]; e != null; e = e.next) {            Object k;            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {                V oldValue = e.value;                e.value = value;                e.recordAccess(this);                return oldValue;            }        }        //没有则新增一个元素,同时modCount计数器+1        modCount++;        addEntry(hash, key, value, i);        return null;    }    private V putForNullKey(V value) {        for (Entry<K,V> e = table[0]; e != null; e = e.next) {            if (e.key == null) {                V oldValue = e.value;                e.value = value;                e.recordAccess(this);                return oldValue;            }        }        modCount++;        addEntry(0, null, value, 0);        return null;    }    /**     * This method is used instead of put by constructors and     * pseudoconstructors (clone, readObject).  It does not resize the table,     * check for comodification, etc.  It calls createEntry rather than     * addEntry.     */    private void putForCreate(K key, V value) {        int hash = null == key ? 0 : hash(key);        int i = indexFor(hash, table.length);        /**         * Look for preexisting entry for key.  This will never happen for         * clone or deserialize.  It will only happen for construction if the         * input Map is a sorted map whose ordering is inconsistent w/ equals.         */        for (Entry<K,V> e = table[i]; e != null; e = e.next) {            Object k;            if (e.hash == hash &&                ((k = e.key) == key || (key != null && key.equals(k)))) {                e.value = value;                return;            }        }        createEntry(hash, key, value, i);    }    private void putAllForCreate(Map<? extends K, ? extends V> m) {        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())            putForCreate(e.getKey(), e.getValue());    }    //rehash 操作    void resize(int newCapacity) {        Entry[] oldTable = table;        int oldCapacity = oldTable.length;        if (oldCapacity == MAXIMUM_CAPACITY) {            threshold = Integer.MAX_VALUE;            return;        }        Entry[] newTable = new Entry[newCapacity];        transfer(newTable, initHashSeedAsNeeded(newCapacity));        table = newTable;        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);    }    //元素转移,rehash参数表示是否重新计算hash值    void transfer(Entry[] newTable, boolean rehash) {        int newCapacity = newTable.length;        for (Entry<K,V> e : table) {            while(null != e) {                Entry<K,V> next = e.next;                if (rehash) {                    e.hash = null == e.key ? 0 : hash(e.key);                }                int i = indexFor(e.hash, newCapacity);                e.next = newTable[i];                newTable[i] = e;                e = next;            }        }    }    /**     * Copies all of the mappings from the specified map to this map.     * These mappings will replace any mappings that this map had for     * any of the keys currently in the specified map.     *     * @param m mappings to be stored in this map     * @throws NullPointerException if the specified map is null     */    public void putAll(Map<? extends K, ? extends V> m) {        int numKeysToBeAdded = m.size();        if (numKeysToBeAdded == 0)            return;        if (table == EMPTY_TABLE) {            inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));        }        /*         * Expand the map if the map if the number of mappings to be added         * is greater than or equal to threshold.  This is conservative; the         * obvious condition is (m.size() + size) >= threshold, but this         * condition could result in a map with twice the appropriate capacity,         * if the keys to be added overlap with the keys already in this map.         * By using the conservative calculation, we subject ourself         * to at most one extra resize.         */        if (numKeysToBeAdded > threshold) {            int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);            if (targetCapacity > MAXIMUM_CAPACITY)                targetCapacity = MAXIMUM_CAPACITY;            int newCapacity = table.length;            while (newCapacity < targetCapacity)                newCapacity <<= 1;            if (newCapacity > table.length)                resize(newCapacity);        }        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())            put(e.getKey(), e.getValue());    }    public V remove(Object key) {        Entry<K,V> e = removeEntryForKey(key);        return (e == null ? null : e.value);    }    final Entry<K,V> removeEntryForKey(Object key) {        if (size == 0) {            return null;        }        int hash = (key == null) ? 0 : hash(key);        int i = indexFor(hash, table.length);        Entry<K,V> prev = table[i];        Entry<K,V> e = prev;        while (e != null) {            Entry<K,V> next = e.next;            Object k;            if (e.hash == hash &&                ((k = e.key) == key || (key != null && key.equals(k)))) {                modCount++;                size--;                if (prev == e)                    table[i] = next;                else                    prev.next = next;                e.recordRemoval(this);                return e;            }            prev = e;            e = next;        }        return e;    }    final Entry<K,V> removeMapping(Object o) {        if (size == 0 || !(o instanceof Map.Entry))            return null;        Map.Entry<K,V> entry = (Map.Entry<K,V>) o;        Object key = entry.getKey();        int hash = (key == null) ? 0 : hash(key);        int i = indexFor(hash, table.length);        Entry<K,V> prev = table[i];        Entry<K,V> e = prev;        while (e != null) {            Entry<K,V> next = e.next;            if (e.hash == hash && e.equals(entry)) {                modCount++;                size--;                if (prev == e)                    table[i] = next;                else                    prev.next = next;                e.recordRemoval(this);                return e;            }            prev = e;            e = next;        }        return e;    }    public void clear() {        modCount++;        Arrays.fill(table, null);        size = 0;    }    public boolean containsValue(Object value) {        if (value == null)            return containsNullValue();        Entry[] tab = table;        for (int i = 0; i < tab.length ; i++)            for (Entry e = tab[i] ; e != null ; e = e.next)                if (value.equals(e.value))                    return true;        return false;    }    private boolean containsNullValue() {        Entry[] tab = table;        for (int i = 0; i < tab.length ; i++)            for (Entry e = tab[i] ; e != null ; e = e.next)                if (e.value == null)                    return true;        return false;    }    public Object clone() {        HashMap<K,V> result = null;        try {            result = (HashMap<K,V>)super.clone();        } catch (CloneNotSupportedException e) {            // assert false;        }        if (result.table != EMPTY_TABLE) {            result.inflateTable(Math.min(                (int) Math.min(                    size * Math.min(1 / loadFactor, 4.0f),                    // we have limits...                    HashMap.MAXIMUM_CAPACITY),               table.length));        }        result.entrySet = null;        result.modCount = 0;        result.size = 0;        result.init();        result.putAllForCreate(this);        return result;    }    static class Entry<K,V> implements Map.Entry<K,V> {        final K key;        V value;        Entry<K,V> next;        int hash;        /**         * Creates new entry.         */        Entry(int h, K k, V v, Entry<K,V> n) {            value = v;            next = n;            key = k;            hash = h;        }        public final K getKey() {            return key;        }        public final V getValue() {            return value;        }        public final V setValue(V newValue) {            V oldValue = value;            value = newValue;            return oldValue;        }        public final boolean equals(Object o) {            if (!(o instanceof Map.Entry))                return false;            Map.Entry e = (Map.Entry)o;            Object k1 = getKey();            Object k2 = e.getKey();            if (k1 == k2 || (k1 != null && k1.equals(k2))) {                Object v1 = getValue();                Object v2 = e.getValue();                if (v1 == v2 || (v1 != null && v1.equals(v2)))                    return true;            }            return false;        }        public final int hashCode() {            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());        }        public final String toString() {            return getKey() + "=" + getValue();        }        /**         * This method is invoked whenever the value in an entry is         * overwritten by an invocation of put(k,v) for a key k that's already         * in the HashMap.         */        void recordAccess(HashMap<K,V> m) {        }        /**         * This method is invoked whenever the entry is         * removed from the table.         */        void recordRemoval(HashMap<K,V> m) {        }    }    //新增元素    void addEntry(int hash, K key, V value, int bucketIndex) {        //rehash的条件,1元素个数大于threshold 2计算出index位置不为空        if ((size >= threshold) && (null != table[bucketIndex])) {            resize(2 * table.length);            hash = (null != key) ? hash(key) : 0;            bucketIndex = indexFor(hash, table.length);        }        createEntry(hash, key, value, bucketIndex);    }    /**     * Like addEntry except that this version is used when creating entries     * as part of Map construction or "pseudo-construction" (cloning,     * deserialization).  This version needn't worry about resizing the table.     *     * Subclass overrides this to alter the behavior of HashMap(Map),     * clone, and readObject.     */    void createEntry(int hash, K key, V value, int bucketIndex) {        Entry<K,V> e = table[bucketIndex];        table[bucketIndex] = new Entry<>(hash, key, value, e);        size++;    }    private abstract class HashIterator<E> implements Iterator<E> {        Entry<K,V> next;        // next entry to return        int expectedModCount;   // For fast-fail        int index;              // current slot        Entry<K,V> current;     // current entry        HashIterator() {            expectedModCount = modCount;            if (size > 0) { // advance to first entry                Entry[] t = table;                while (index < t.length && (next = t[index++]) == null)                    ;            }        }        public final boolean hasNext() {            return next != null;        }        final Entry<K,V> nextEntry() {            if (modCount != expectedModCount)                throw new ConcurrentModificationException();            Entry<K,V> e = next;            if (e == null)                throw new NoSuchElementException();            if ((next = e.next) == null) {                Entry[] t = table;                while (index < t.length && (next = t[index++]) == null)                    ;            }            current = e;            return e;        }        public void remove() {            if (current == null)                throw new IllegalStateException();            if (modCount != expectedModCount)                throw new ConcurrentModificationException();            Object k = current.key;            current = null;            HashMap.this.removeEntryForKey(k);            expectedModCount = modCount;        }    }    private final class ValueIterator extends HashIterator<V> {        public V next() {            return nextEntry().value;        }    }    private final class KeyIterator extends HashIterator<K> {        public K next() {            return nextEntry().getKey();        }    }    private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {        public Map.Entry<K,V> next() {            return nextEntry();        }    }    // Subclass overrides these to alter behavior of views' iterator() method    Iterator<K> newKeyIterator()   {        return new KeyIterator();    }    Iterator<V> newValueIterator()   {        return new ValueIterator();    }    Iterator<Map.Entry<K,V>> newEntryIterator()   {        return new EntryIterator();    }    // Views    private transient Set<Map.Entry<K,V>> entrySet = null;    /**     * 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.     */    public Set<K> keySet() {        Set<K> ks = keySet;        return (ks != null ? ks : (keySet = new KeySet()));    }    private final class KeySet extends AbstractSet<K> {        public Iterator<K> iterator() {            return newKeyIterator();        }        public int size() {            return size;        }        public boolean contains(Object o) {            return containsKey(o);        }        public boolean remove(Object o) {            return HashMap.this.removeEntryForKey(o) != null;        }        public void clear() {            HashMap.this.clear();        }    }    /**     * Returns a {@link Collection} view of the values contained in this map.     * The collection is backed by the map, so changes to the map are     * reflected in the collection, and vice-versa.  If the map is     * modified while an iteration over the collection is in progress     * (except through the iterator's own <tt>remove</tt> operation),     * the results of the iteration are undefined.  The collection     * supports element removal, which removes the corresponding     * mapping from the map, via the <tt>Iterator.remove</tt>,     * <tt>Collection.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.     */    public Collection<V> values() {        Collection<V> vs = values;        return (vs != null ? vs : (values = new Values()));    }    private final class Values extends AbstractCollection<V> {        public Iterator<V> iterator() {            return newValueIterator();        }        public int size() {            return size;        }        public boolean contains(Object o) {            return containsValue(o);        }        public void clear() {            HashMap.this.clear();        }    }    /**     * Returns a {@link Set} view of the mappings 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, or through the     * <tt>setValue</tt> operation on a map entry returned by the     * iterator) 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.     *     * @return a set view of the mappings contained in this map     */    public Set<Map.Entry<K,V>> entrySet() {        return entrySet0();    }    private Set<Map.Entry<K,V>> entrySet0() {        Set<Map.Entry<K,V>> es = entrySet;        return es != null ? es : (entrySet = new EntrySet());    }    private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {        public Iterator<Map.Entry<K,V>> iterator() {            return newEntryIterator();        }        public boolean contains(Object o) {            if (!(o instanceof Map.Entry))                return false;            Map.Entry<K,V> e = (Map.Entry<K,V>) o;            Entry<K,V> candidate = getEntry(e.getKey());            return candidate != null && candidate.equals(e);        }        public boolean remove(Object o) {            return removeMapping(o) != null;        }        public int size() {            return size;        }        public void clear() {            HashMap.this.clear();        }    }    /**     * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,     * serialize it).     *     * @serialData The <i>capacity</i> of the HashMap (the length of the     *             bucket array) is emitted (int), followed by the     *             <i>size</i> (an int, the number of key-value     *             mappings), followed by the key (Object) and value (Object)     *             for each key-value mapping.  The key-value mappings are     *             emitted in no particular order.     */    private void writeObject(java.io.ObjectOutputStream s)        throws IOException    {        // Write out the threshold, loadfactor, and any hidden stuff        s.defaultWriteObject();        // Write out number of buckets        if (table==EMPTY_TABLE) {            s.writeInt(roundUpToPowerOf2(threshold));        } else {           s.writeInt(table.length);        }        // Write out size (number of Mappings)        s.writeInt(size);        // Write out keys and values (alternating)        if (size > 0) {            for(Map.Entry<K,V> e : entrySet0()) {                s.writeObject(e.getKey());                s.writeObject(e.getValue());            }        }    }    private static final long serialVersionUID = 362498820763181265L;    /**     * Reconstitute the {@code HashMap} instance from a stream (i.e.,     * deserialize it).     */    private void readObject(java.io.ObjectInputStream s)         throws IOException, ClassNotFoundException    {        // Read in the threshold (ignored), loadfactor, and any hidden stuff        s.defaultReadObject();        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {            throw new InvalidObjectException("Illegal load factor: " +                                               loadFactor);        }        // set other fields that need values        table = (Entry<K,V>[]) EMPTY_TABLE;        // Read in number of buckets        s.readInt(); // ignored.        // Read number of mappings        int mappings = s.readInt();        if (mappings < 0)            throw new InvalidObjectException("Illegal mappings count: " +                                               mappings);        // capacity chosen by number of mappings and desired load (if >= 0.25)        int capacity = (int) Math.min(                    mappings * Math.min(1 / loadFactor, 4.0f),                    // we have limits...                    HashMap.MAXIMUM_CAPACITY);        // allocate the bucket array;        if (mappings > 0) {            inflateTable(capacity);        } else {            threshold = capacity;        }        init();  // Give subclass a chance to do its thing.        // Read the keys and values, and put the mappings in the HashMap        for (int i = 0; i < mappings; i++) {            K key = (K) s.readObject();            V value = (V) s.readObject();            putForCreate(key, value);        }    }    // These methods are used when serializing HashSets    int   capacity()     { return table.length; }    float loadFactor()   { return loadFactor;   }}

HashSet源码清单

/* * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */package java.util;/** * This class implements the <tt>Set</tt> interface, backed by a hash table * (actually a <tt>HashMap</tt> instance).  It makes no guarantees as to the * iteration order of the set; in particular, it does not guarantee that the * order will remain constant over time.  This class permits the <tt>null</tt> * element. * * <p>This class offers constant time performance for the basic operations * (<tt>add</tt>, <tt>remove</tt>, <tt>contains</tt> and <tt>size</tt>), * assuming the hash function disperses the elements properly among the * buckets.  Iterating over this set requires time proportional to the sum of * the <tt>HashSet</tt> instance's size (the number of elements) plus the * "capacity" of the backing <tt>HashMap</tt> instance (the number of * buckets).  Thus, it's very important not to set the initial capacity too * high (or the load factor too low) if iteration performance is important. * * <p><strong>Note that this implementation is not synchronized.</strong> * If multiple threads access a hash set concurrently, and at least one of * the threads modifies the set, it <i>must</i> be synchronized externally. * This is typically accomplished by synchronizing on some object that * naturally encapsulates the set. * * If no such object exists, the set should be "wrapped" using the * {@link Collections#synchronizedSet Collections.synchronizedSet} * method.  This is best done at creation time, to prevent accidental * unsynchronized access to the set:<pre> *   Set s = Collections.synchronizedSet(new HashSet(...));</pre> * * <p>The iterators returned by this class's <tt>iterator</tt> method are * <i>fail-fast</i>: if the set is modified at any time after the iterator is * created, in any way except through the iterator's own <tt>remove</tt> * method, the Iterator throws a {@link 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. * * <p>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 <tt>ConcurrentModificationException</tt> on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this * exception for its correctness: <i>the fail-fast behavior of iterators * should be used only to detect bugs.</i> * * <p>This class is a member of the * <a href="{@docRoot}/../technotes/guides/collections/index.html"> * Java Collections Framework</a>. * * @param <E> the type of elements maintained by this set * * @author  Josh Bloch * @author  Neal Gafter * @see     Collection * @see     Set * @see     TreeSet * @see     HashMap * @since   1.2 */public class HashSet<E>    extends AbstractSet<E>    implements Set<E>, Cloneable, java.io.Serializable{    static final long serialVersionUID = -5024744406713321676L;    private transient HashMap<E,Object> map;    // Dummy value to associate with an Object in the backing Map    private static final Object PRESENT = new Object();    /**     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has     * default initial capacity (16) and load factor (0.75).     */    public HashSet() {        map = new HashMap<>();    }    /**     * Constructs a new set containing the elements in the specified     * collection.  The <tt>HashMap</tt> is created with default load factor     * (0.75) and an initial capacity sufficient to contain the elements in     * the specified collection.     *     * @param c the collection whose elements are to be placed into this set     * @throws NullPointerException if the specified collection is null     */    public HashSet(Collection<? extends E> c) {        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));        addAll(c);    }    /**     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has     * the specified initial capacity and the specified load factor.     *     * @param      initialCapacity   the initial capacity of the hash map     * @param      loadFactor        the load factor of the hash map     * @throws     IllegalArgumentException if the initial capacity is less     *             than zero, or if the load factor is nonpositive     */    public HashSet(int initialCapacity, float loadFactor) {        map = new HashMap<>(initialCapacity, loadFactor);    }    /**     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has     * the specified initial capacity and default load factor (0.75).     *     * @param      initialCapacity   the initial capacity of the hash table     * @throws     IllegalArgumentException if the initial capacity is less     *             than zero     */    public HashSet(int initialCapacity) {        map = new HashMap<>(initialCapacity);    }    /**     * Constructs a new, empty linked hash set.  (This package private     * constructor is only used by LinkedHashSet.) The backing     * HashMap instance is a LinkedHashMap with the specified initial     * capacity and the specified load factor.     *     * @param      initialCapacity   the initial capacity of the hash map     * @param      loadFactor        the load factor of the hash map     * @param      dummy             ignored (distinguishes this     *             constructor from other int, float constructor.)     * @throws     IllegalArgumentException if the initial capacity is less     *             than zero, or if the load factor is nonpositive     */    HashSet(int initialCapacity, float loadFactor, boolean dummy) {        map = new LinkedHashMap<>(initialCapacity, loadFactor);    }    /**     * Returns an iterator over the elements in this set.  The elements     * are returned in no particular order.     *     * @return an Iterator over the elements in this set     * @see ConcurrentModificationException     */    public Iterator<E> iterator() {        return map.keySet().iterator();    }    /**     * Returns the number of elements in this set (its cardinality).     *     * @return the number of elements in this set (its cardinality)     */    public int size() {        return map.size();    }    /**     * Returns <tt>true</tt> if this set contains no elements.     *     * @return <tt>true</tt> if this set contains no elements     */    public boolean isEmpty() {        return map.isEmpty();    }    /**     * Returns <tt>true</tt> if this set contains the specified element.     * More formally, returns <tt>true</tt> if and only if this set     * contains an element <tt>e</tt> such that     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.     *     * @param o element whose presence in this set is to be tested     * @return <tt>true</tt> if this set contains the specified element     */    public boolean contains(Object o) {        return map.containsKey(o);    }    /**     * Adds the specified element to this set if it is not already present.     * More formally, adds the specified element <tt>e</tt> to this set if     * this set contains no element <tt>e2</tt> such that     * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.     * If this set already contains the element, the call leaves the set     * unchanged and returns <tt>false</tt>.     *     * @param e element to be added to this set     * @return <tt>true</tt> if this set did not already contain the specified     * element     */    public boolean add(E e) {        return map.put(e, PRESENT)==null;    }    /**     * Removes the specified element from this set if it is present.     * More formally, removes an element <tt>e</tt> such that     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>,     * if this set contains such an element.  Returns <tt>true</tt> if     * this set contained the element (or equivalently, if this set     * changed as a result of the call).  (This set will not contain the     * element once the call returns.)     *     * @param o object to be removed from this set, if present     * @return <tt>true</tt> if the set contained the specified element     */    public boolean remove(Object o) {        return map.remove(o)==PRESENT;    }    /**     * Removes all of the elements from this set.     * The set will be empty after this call returns.     */    public void clear() {        map.clear();    }    /**     * Returns a shallow copy of this <tt>HashSet</tt> instance: the elements     * themselves are not cloned.     *     * @return a shallow copy of this set     */    public Object clone() {        try {            HashSet<E> newSet = (HashSet<E>) super.clone();            newSet.map = (HashMap<E, Object>) map.clone();            return newSet;        } catch (CloneNotSupportedException e) {            throw new InternalError();        }    }    /**     * Save the state of this <tt>HashSet</tt> instance to a stream (that is,     * serialize it).     *     * @serialData The capacity of the backing <tt>HashMap</tt> instance     *             (int), and its load factor (float) are emitted, followed by     *             the size of the set (the number of elements it contains)     *             (int), followed by all of its elements (each an Object) in     *             no particular order.     */    private void writeObject(java.io.ObjectOutputStream s)        throws java.io.IOException {        // Write out any hidden serialization magic        s.defaultWriteObject();        // Write out HashMap capacity and load factor        s.writeInt(map.capacity());        s.writeFloat(map.loadFactor());        // Write out size        s.writeInt(map.size());        // Write out all elements in the proper order.        for (E e : map.keySet())            s.writeObject(e);    }    /**     * Reconstitute the <tt>HashSet</tt> instance from a stream (that is,     * deserialize it).     */    private void readObject(java.io.ObjectInputStream s)        throws java.io.IOException, ClassNotFoundException {        // Read in any hidden serialization magic        s.defaultReadObject();        // Read in HashMap capacity and load factor and create backing HashMap        int capacity = s.readInt();        float loadFactor = s.readFloat();        map = (((HashSet)this) instanceof LinkedHashSet ?               new LinkedHashMap<E,Object>(capacity, loadFactor) :               new HashMap<E,Object>(capacity, loadFactor));        // Read in size        int size = s.readInt();        // Read in all elements in the proper order.        for (int i=0; i<size; i++) {            E e = (E) s.readObject();            map.put(e, PRESENT);        }    }}
0 0
原创粉丝点击