HashMap源码详解(JDK7版本)

一、内部属性

内部属性源码:

    //内部数组的默认初始容量,作为hashmap的初始容量,是2的4次方,2的n次方的作用是减少hash冲突
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    //默认的最大容量
    static final int MAXIMUM_CAPACITY = 1 << 30;

    //默认负载因子,当容器使用率达到这个75%的时候就扩容
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     *当数组表还没扩容的时候,一个共享的空表对象
     */
    static final Entry<?,?>[] EMPTY_TABLE = {};

    //内部数组表,用来装entry,大小只能是2的n次方。
    transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

    //存储的键值对的个数
    transient int size;

    /**
     * 扩容的临界点,如果当前容量达到该值,则需要扩容了。
     * 如果当前数组容量为0时(空数组),则该值作为初始化内部数组的初始容量
     */
    int threshold;

    //由构造函数传入的指定负载因子
    final float loadFactor;

    //Hash的修改次数
    transient int modCount;

    //threshold的最大值
    static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

    //计算hash值时候用,初始是0
    transient int hashSeed = 0;

    //含有所有entry节点的一个set集合
    private transient Set<Map.Entry<K,V>> entrySet = null;

    private static final long serialVersionUID = 362498820763181265L;

内部类Entry源码分析:

/**
     * 内部类
     * hashmap中每一个键值对都是存在Entry对象中,entry还存储了自己的hash值等信息
     * Entry被储存在hashmap的内部数组中。
     * @param <K> 键值名key
     * @param <V> 键值value
     */
    static class Entry<K,V> implements Map.Entry<K,V> {
        //键值名
        final K key;
        //键值
        V value;
        //数组中每一项可能存储多个entry,而这些entry就是已链表的形式被存储,此next指向下一个entry
        Entry<K,V> next;
        //本entry的hash值
        int hash;

        //初始化节点
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
        //获取节点的key
        public final K getKey() {
            return key;
        }

        //获取节点的value
        public final V getValue() {
            return value;
        }

        //设置新value,并返回旧的value
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        //判断传入节点与此结点的“key”和“value”是否相等。都相等则返回true
        public final boolean equals(Object o) {
            //如果传入对象不是Entry,则返回false
            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;
        }

        //根据key和value的值生成hashCode
        public final int hashCode() {
            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
        }

        public final String toString() {
            return getKey() + "=" + getValue();
        }

        //每当相同key的value被覆盖时被调用一次,在HashMap的子类LinkedHashMap中实现了这个方法
        void recordAccess(HashMap<K,V> m) {
        }

        //每移除一个entry就被调用一次,在HashMap的子类LinkedHashMap中实现了这个方法;
        void recordRemoval(HashMap<K,V> m) {
        }
    }

二、构造方法

构造源码分析:

    /**
     * 生成一个空HashMap,传入容量与负载因子
     * @param initialCapacity 初始容量
     * @param loadFactor 负载因子
     */
    public HashMap(int initialCapacity, float loadFactor) {
        //初始容量不能小于0
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                    initialCapacity);
        //初始容量不能大于默认的最大容量
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;

        //负载因子不能小于0,且不能为“NaN”(NaN(“不是一个数字(Not a Number)”的缩写))
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                    loadFactor);
        //将传入的负载因子赋值给属性
        this.loadFactor = loadFactor;

        //此时并不会创建容器,因为没有 传具体值
        // 没下次扩容大小
        /**
         * 此时并不会创建容器,因为没有传具体值。
         * 当下次传具体值的时候,才会“根据这次的初始容量”,创建一个内部数组。
         * 所以此次的初始容量只是作为下一次扩容(新建)的容量。
         */
        threshold = initialCapacity;

        //该方法只在LinkedHashMap中有实现,主要在构造函数初始化和clone、readObject中有调用。
        init();
    }

    /**
     * 生成一个空hashmap,传入初始容量,负载因子使用默认值(0.75)
     * @param initialCapacity 初始容量
     */
    public HashMap(int initialCapacity) {
        //生成空数组,并指定扩容值
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    /**
     * 生成一个空hashmap,初始容量和负载因子全部使用默认值。
     */
    public HashMap() {
        //生成空数组,并指定扩容值
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

    /**
     * 根据已有map对象生成一个hashmap,初始容量与传入的map相关,负载因子使用默认值
     * @param m Map对象
     */
    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);

        //将传入map的键值对添加到初始数组中
        putAllForCreate(m);
    }

相关private方法源码分析:

    /**
     * 只在LinkedHashMap中有实现,主要在构造函数初始化和clone、readObject中有调用。
     */
    void init() {
    }

    /**
     * 新建一个空的内部数组
     * @param toSize 新数组容量
     */
    private void inflateTable(int toSize) {
        //内部数组的大小必须是2的n次方,所以要找到“大于”toSize的“最小的2的n次方”。
        int capacity = roundUpToPowerOf2(toSize);

        //下次扩容临界值
        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);

        table = new Entry[capacity];

        //根据数组长度初始化hashseed
        initHashSeedAsNeeded(capacity);
    }

    /**
     * 找到number的最小的2的n次方
     * @param number
     * @return
     */
    private static int roundUpToPowerOf2(int number) {

        return number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
    }

    /**
     * 根据内部数组长度初始化hashseed
     * @param capacity 内部数组长度
     * @return hashSeed是否初始化
     */
    final boolean initHashSeedAsNeeded(int capacity) {
        boolean currentAltHashing = hashSeed != 0;
        boolean useAltHashing = sun.misc.VM.isBooted() && (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean switching = currentAltHashing ^ useAltHashing;

        //为true则赋初始化值
        if (switching) {
            hashSeed = useAltHashing
                    ? sun.misc.Hashing.randomHashSeed(this)
                    : 0;
        }
        return switching;
    }

    /**
     * 静态内部类,提供一些静态常量
     */
    private static class Holder {

        /**
         * 容量阈值,初始化hashSeed的时候会用到该值
         */
        static final int ALTERNATIVE_HASHING_THRESHOLD;

        static {
            //获取系统变量jdk.map.althashing.threshold
            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;

                // jdk.map.althashing.threshold系统变量默认为-1,如果为-1,则将阈值设为Integer.MAX_VALUE
                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;
        }
    }

    /**
     * 添加指定map里面的所有键值对
     * @param m
     */
    private void putAllForCreate(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            putForCreate(e.getKey(), e.getValue());
    }

    /**
     * 添加键值对
     * @param key 键值名
     * @param value 键值
     */
    private void putForCreate(K key, V value) {
        //如果key为null,则hash值为0,否则根据key计算hash值
        int hash = null == key ? 0 : hash(key);

        //根据hash值和数组的长度找到:该key所属entry在table中的位置i
        int i = indexFor(hash, table.length);

        /**
         * 数组中每一项存的都是一个链表,
         * 先找到i位置,然后循环该位置上的每一个entry,
         * 如果发现存在key与传入key相等,则替换其value。然后结束侧方法。
         * 如果没有找到相同的key,则继续执行下一条指令,将此键值对存入链表头
         */
        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);
    }

    /**
     * 根据传入的key生成hash值
     * @param k  键值名
     * @return hash值
     */
    final int hash(Object k) {
        int h = hashSeed;

        //如果key是字符串类型,就使用stringHash32来生成hash值
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        //一次散列
        h ^= k.hashCode();

        //二次散列
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

    /**
     * 返回hash值的索引,采用除模取余法,h & (length-1)操作 等价于 hash % length操作, 但&操作性能更优
     */
    /**
     * 根据key的hash值与数组长度,找到该key在table数组中的下标
     * @param h hash值
     * @param length 数组长度
     * @return 下标
     */
    static int indexFor(int h, int length) {
        //除模取余,相当于hash % length,&速度更快
        return h & (length-1);
    }

    /**
     * 将键值对与他的hash值作为一个entry,插入table的指定下标中的链表头中
     * @param hash hash值
     * @param key 键值名
     * @param value 键值
     * @param bucketIndex 被插入的下标
     */
    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++;
    }

三、存储

public方法源码分析:

    /**
     * 存入一个键值对,如果key重复,则更新value
     * @param key 键值名
     * @param value 键值
     * @return 如果存的是新key则返回null,如果覆盖了旧键值对,则返回旧value
     */
    public V put(K key, V value) {
        //如果数组为空,则新建数组
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }

        //如果key为null,则把value放在table[0]中
        if (key == null)
            return putForNullKey(value);

        //生成key所对应的hash值
        int hash = hash(key);

        //根据hash值和数组的长度找到:该key所属entry在table中的位置i
        int i = indexFor(hash, table.length);

        /**
         * 数组中每一项存的都是一个链表,
         * 先找到i位置,然后循环该位置上的每一个entry,
         * 如果发现存在key与传入key相等,则替换其value。然后结束侧方法。
         * 如果没有找到相同的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;
            }
        }

        //map操作次数加一
        modCount++;

        //查看是否需要扩容,并将该键值对存入指定下标的链表头中
        addEntry(hash, key, value, i);

        //如果是新存入的键值对,则返回null
        return null;
    }

    /**
     * 将传入map的所有键值对存入本map
     * @param m 传入map
     */
    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));
        }

        //如果传入map的键值对数比“下一次扩容后的内部数组大小”还大,则对数组进行扩容。(因为当前数组即使扩容后也装不下它)
        if (numKeysToBeAdded > threshold) {
            //确定新内部数组所需容量
            int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
            //不能大于最大容量
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            //当前数组长度
            int newCapacity = table.length;
            //从当前数组长度开始增加,每次增加一个“2次方”,直到大于所需容量为止
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;

            //如果发现内部数组长度需要增加,则扩容内部数组
            if (newCapacity > table.length)
                resize(newCapacity);
        }

        //遍历传入map,将键值对存入内部数组
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

相关private源码分析:

    /**
     * 如果key为null,则将其value存入table[0]的链表中
     * @param value 键值
     * @return 如果覆盖了旧value,则返回value,否则返回null
     */
    private V putForNullKey(V value) {
        //迭代table[0]中的链表里的每一个entry
        for (Entry<K, V> e = table[0]; e != null; e = e.next) {
            //如果找到key为null的entry,则覆盖其value,并返回旧value
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        //操作次数加一
        modCount++;

        //查看是否需要扩容,然后将entry插入table的指定下标中的链表头中
        addEntry(0, null, value, 0);
        return null;
    }

    /**
     * 查看是否需要扩容,然后添加新节点
     * @param hash key的hash值
     * @param key 结点内key
     * @param value 结点内value
     * @param bucketIndex 结点所在的table下标
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        //如果当前键值对数量达到了临界值,或目标table下标不存在,则扩容table
        if ((size >= threshold) && (null != table[bucketIndex])) {
            //容量扩容一倍
            resize(2 * table.length);
            //由于数组扩容了,重新计算hash值
            hash = (null != key) ? hash(key) : 0;
            //重新计算存储位置
            bucketIndex = indexFor(hash, table.length);
        }

        //将键值对与他的hash值作为一个entry,插入table的指定下标中的链表头中
        createEntry(hash, key, value, bucketIndex);
    }

    /**
     * 对数组扩容,即创建一个新数组,并将旧数组里的东西重新存入新数组
     * @param newCapacity 新数组容量
     */
    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;

        //如果当前数组容量已经达到最大值了,则将扩容的临界值设置为Integer.MAX_VALUE(Integer.MAX_VALUE是容量的临界点)
        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);
    }

    /**
     * 将现有数组中的内容重新通过hash计算存入新数组
     * @param newTable 新数组
     * @param rehash
     */
    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;

        //遍历现有数组中的每一个单链表的头entry
        for (Entry<K,V> e : table) {
            //查找链表里的每一个entry
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }

                //根据新的数组长度,重新计算此entry所在下标i
                int i = indexFor(e.hash, newCapacity);

                //将entry放入下标i处链表的头部(将新数组此处的原有链表存入entry的next指针)
                e.next = newTable[i];

                //将链表存回下标i
                newTable[i] = e;

                //查看下一个entry
                e = next;
            }
        }
    }

四、提取

public方法源码分析:

    /**
     * 返回此hashmap中存储的键值对个数
     * @return 键值对个数
     */
    public int size() {
        return size;
    }

    /**
     * 根据key找到对应value
     * @param key 键值名
     * @return 键值value
     */
    public V get(Object key) {
        //如果key为null,则从table[0]中取value
        if (key == null)
            return getForNullKey();

        //如果key不为null,则先根据key,找到其entry
        Entry<K,V> entry = getEntry(key);

        //返回entry节点里的value值
        return null == entry ? null : entry.getValue();
    }

    /**
     * 返回一个set集合,里面装的都是hashmap的value。
     * 因为map中的key不能重复,set集合中的值也不能重复,所以可以装入set。
     *
     * 在hashmap的父类AbstractMap中,定义了Set<K> keySet = null;
     * 如果keySet为null,则返回内部类KeySet。
     * @return 含有所有key的set集合
     */
    public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }

    /**
     * 返回一个Collection集合,里面装的都是hashmap的value。
     * 因为map中的value可以重复,所以装入Collection。
     *
     * 在hashmap的父类AbstractMap中,定义了Collection<V> values = null;
     * 如果values为null,则返回内部类Values。
     */
    public Collection<V> values() {
        Collection<V> vs = values;
        return (vs != null ? vs : (values = new Values()));
    }

    /**
     * 返回一个set集合,里面装的是所有的entry结点
     * (相当于把map集合转化成set集合)
     * @return 含有所有entry的set集合
     */
    public Set<Map.Entry<K,V>> entrySet() {
        return entrySet0();
    }

    /**
     * 生成一个新的hashmap对象,新hashmap中数组也是新生成的,
     * 但数组中的entry节点还是引用就hashmap中的元素。
     * 所以对目前已有的节点进行修改会导致:原对象和clone对象都发生改变。
     * 但进行新增或删除就不会影响对方,因为这相当于是对数组做出的改变,clone对象新生成了一个数组。
     * @return clone出的hashmap
     */
    public Object clone() {
        HashMap<K,V> result = null;
        try {
            result = (HashMap<K,V>)super.clone();
        } catch (CloneNotSupportedException e) {
        }
        if (result.table != EMPTY_TABLE) {
            result.inflateTable(Math.min(
                    (int) Math.min(
                            size * Math.min(1 / loadFactor, 4.0f),

                            HashMap.MAXIMUM_CAPACITY),
                    table.length));
        }
        result.entrySet = null;
        result.modCount = 0;
        result.size = 0;
        result.init();
        result.putAllForCreate(this);

        return result;
    }

相关private源码分析:

    /**
     * 查找key为null的value
     * (如果key为null,则hash值为0,并被保存在table[0]中)
     * @return 对应value
     */
    private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        //查找table[0]处的链表,如果找到entry的key为null,就返回其value
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

    /**
     * 根据key值查找所属entry节点
     * @param key 键值名
     * @return entry节点
     */
    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        //如果key为null,则其hash值为0,否则计算hash值
        int hash = (key == null) ? 0 : hash(key);

        //根据hash值找到table下标,然后迭代该下标中的链表里的每一个entry节点
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            //如果找到该节点则返回该节点
            if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

    /**
     * 内部类,生成一个set集合,里面装有此hashmap的所有key。
     */
    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();
        }
    }

    /**
     * 内部类,生成一个Collection集合,里面装有此hashmap的所有value
     */
    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();
        }
    }

    /**
     * 如果entrySet为null,则返回一个含有所有entry节点的一个set集合
     * @return 含有所有entry节点的一个set集合
     */
    private Set<Map.Entry<K,V>> entrySet0() {
        Set<Map.Entry<K,V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }

    /**
     * 内部类,含有所有entry节点的一个set集合
     */
    private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        //返回迭代器
        public Iterator<Map.Entry<K,V>> iterator() {
            return newEntryIterator();
        }

        //查找传入entry是否存在
        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();
        }
    }

    /**
     * 删除指定entry节点
     * @param o 需要被删除的节点
     * @return 如果删除失败返回null,删除成功
     */
    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方法源码分析:

    /**
     * 判断hashmap是否为空
     * @return true为空,false为非空
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 判断指定key是否存在
     * @param key 键值名
     * @return 存在则返回true
     */
    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

    /**
     * 判断是否含有指定value
     * @param value 键值
     * @return 含有则返回true
     */
    public boolean containsValue(Object value) {
        //如果value为null,则判断是否含有value为null的键值对
        if (value == null)
            return containsNullValue();

        Entry[] tab = table;
        //遍历table,找到每条链表
        for (int i = 0; i < tab.length ; i++)
            //遍历每条单链表,查看每一个entry的value是否为传入值
            for (Entry e = tab[i] ; e != null ; e = e.next)
                if (value.equals(e.value))
                    return true;
        return false;
    }

相关private源码分析:

    /**
     * 判断是否含有value为null的键值对
     * @return 含有则返回true
     */
    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方法源码分析:

    /**
     * 根据key删除entry节点
     * @param key 被删除的entry的key值
     * @return 被删除的节点的value,删除失败则返回null
     */
    public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }

    /**
     * 删除hashmap中的所有元素
     */
    public void clear() {
        modCount++;
        //将table中的每一个元素都设置成null
        Arrays.fill(table, null);
        size = 0;
    }

相关private源码分析:

    /**
     * 根据key删除entry节点
     * @param key 被删除的entry的key值
     * @return 被删除的节点,删除失败则返回null
     */
    final Entry<K,V> removeEntryForKey(Object key) {
        if (size == 0) {
            return null;
        }
        //计算key的hash值
        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;
    }

七、内部迭代器相关源码分析:

    // 返回各种迭代器对象
    Iterator<K> newKeyIterator()   {
        return new KeyIterator();
    }
    Iterator<V> newValueIterator()   {
        return new ValueIterator();
    }
    Iterator<Map.Entry<K,V>> newEntryIterator()   {
        return new EntryIterator();
    }

    //ValueIterator迭代器
    private final class ValueIterator extends HashIterator<V> {
        public V next() {
            return nextEntry().value;
        }
    }
    //KeyIterator迭代器
    private final class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }
    //KeyIterator迭代器
    private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
        public Map.Entry<K,V> next() {
            return nextEntry();
        }
    }

    /**
     * 所有迭代器的抽象父类
     * @param <E> 存储的数据的类型
     */
    private abstract class HashIterator<E> implements Iterator<E> {
        //指向下一个节点
        Entry<K,V> next;
        //
        int expectedModCount;   // 用于判断快速失败行为
        //当前table下标
        int index;
        //当前entry节点
        Entry<K,V> current;

        //构造函数,

        /**
         * 构造函数
         * 使expectedModCount = modCount相等
         */
        HashIterator() {
            expectedModCount = modCount;
            if (size > 0) {
                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;
        }
    }
时间: 2024-10-09 19:49:24

HashMap源码详解(JDK7版本)的相关文章

深入Java基础(四)--哈希表(1)HashMap应用及源码详解

继续深入Java基础系列.今天是研究下哈希表,毕竟我们很多应用层的查找存储框架都是哈希作为它的根数据结构进行封装的嘛. 本系列: (1)深入Java基础(一)--基本数据类型及其包装类 (2)深入Java基础(二)--字符串家族 (3)深入Java基础(三)–集合(1)集合父类以及父接口源码及理解 (4)深入Java基础(三)–集合(2)ArrayList和其继承树源码解析以及其注意事项 文章结构:(1)哈希概述及HashMap应用:(2)HashMap源码分析:(3)再次总结关键点 一.哈希概

Spring IOC源码详解之容器依赖注入

Spring IOC源码详解之容器依赖注入 上一篇博客中介绍了IOC容器的初始化,通过源码分析大致了解了IOC容器初始化的一些知识,先简单回顾下上篇的内容 载入bean定义文件的过程,这个过程是通过BeanDefinitionReader来完成的,其中通过 loadBeanDefinition()来对定义文件进行解析和根据Spring定义的bean规则进行处理 - 事实上和Spring定义的bean规则相关的处理是在BeanDefinitionParserDelegate中完成的,完成这个处理需

Spring IOC源码详解之容器初始化

Spring IOC源码详解之容器初始化 上篇介绍了Spring IOC的大致体系类图,先来看一段简短的代码,使用IOC比较典型的代码 ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDe

Android ArrayMap源码详解

尊重原创,转载请标明出处    http://blog.csdn.net/abcdef314159 分析源码之前先来介绍一下ArrayMap的存储结构,ArrayMap数据的存储不同于HashMap和SparseArray,在上一篇<Android SparseArray源码详解>中我们讲到SparseArray是以纯数组的形式存储的,一个数组存储的是key值一个数组存储的是value值,今天我们分析的ArrayMap和SparseArray有点类似,他也是以纯数组的形式存储,不过不同的是他的

《GIS软件ShapMap源码详解及应用》概述

我喜欢GIS二次开发,即使有的人看不起:我不懂开源GIS,只会点商业的GIS,有的人更加瞧不起.我认为,我不能改变现实这个环境,但可以创造一些价值.找到一本<GIS软件ShapMap源码详解及应用>来学习,我倒要看看开源GIS是什么样子. 当前GIS软件有商业GIS系统及开源GIS系统之分.GIS商用软件功能强 大,有完善的技术支持,提供封装好的.功能强大的类库,基于商用GIS库进 行的二次开发效率高.难度低.资源丰富.但对于小型GIS开发人员,商用 GIS价格过高,对于GIS学习者来说,由于

Android编程之Fragment动画加载方法源码详解

上次谈到了Fragment动画加载的异常问题,今天再聊聊它的动画加载loadAnimation的实现源代码: Animation loadAnimation(Fragment fragment, int transit, boolean enter, int transitionStyle) { 接下来具体看一下里面的源码部分,我将一部分一部分的讲解,首先是: Animation animObj = fragment.onCreateAnimation(transit, enter, fragm

Java concurrent AQS 源码详解

一.引言 AQS(同步阻塞队列)是concurrent包下锁机制实现的基础,相信大家在读完本篇博客后会对AQS框架有一个较为清晰的认识 这篇博客主要针对AbstractQueuedSynchronizer的源码进行分析,大致分为三个部分: 静态内部类Node的解析 重要常量以及字段的解析 重要方法的源码详解. 所有的分析仅基于个人的理解,若有不正之处,请谅解和批评指正,不胜感激!!! 二.Node解析 AQS在内部维护了一个同步阻塞队列,下面简称sync queue,该队列的元素即静态内部类No

IntentService源码详解

IntentService可以做什么: 如果你有一个任务,分成n个子任务,需要它们按照顺序完成.如果需要放到一个服务中完成,那么IntentService就会使最好的选择. IntentService是什么: IntentService是一个Service(看起来像废话,但是我第一眼看到这个名字,首先注意的是Intent啊.),所以如果自定义一个IntentService的话,一定要在AndroidManifest.xml里面声明. 从上面的"可以做什么"我们大概可以猜测一下Inten

Android View 事件分发机制源码详解(View篇)

前言 在Android View 事件分发机制源码详解(ViewGroup篇)一文中,主要对ViewGroup#dispatchTouchEvent的源码做了相应的解析,其中说到在ViewGroup把事件传递给子View的时候,会调用子View的dispatchTouchEvent,这时分两种情况,如果子View也是一个ViewGroup那么再执行同样的流程继续把事件分发下去,即调用ViewGroup#dispatchTouchEvent:如果子View只是单纯的一个View,那么调用的是Vie