Java7/8集合框架——HashMap

Posted wpbxin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java7/8集合框架——HashMap相关的知识,希望对你有一定的参考价值。

java.util.HashMap

  Java7/8中HashMap(和 ConcurrentHashMap)的相关基本操作源码介绍,这里可以直接参考【Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析,介绍得还是挺详细的,就不班门弄斧了。

  关于Java7的HashMap,这里主要提几点,具体的分析还是请参考上面给的链接,

  1. Java7中HashMap源码的大方向就是:数组 + 单向链表(数组的元素,Entry 实例,包含四个属性:key, value, hash 值和用于单向链表的 next),对于hash冲突的元素,使用链表进行存储,每次存储在链表头,操作也比较快。
  2. capacity:当前数组容量,默认值是16,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。
  3. loadFactor:负载因子,默认为 0.75。
  4. threshold:扩容的阈值,等于 capacity * loadFactor,当元素实际个数size大于等于threshold时,进行扩容。

  Java8的HashMap,最大的改变,是使用了数组+链表+红黑树。当链表中的元素达到了 8 个时,会将链表转换为红黑树,在这些位置进行查找的时候可以由原来的耗时O(N),降低时间复杂度为 O(logN)。

  关于红黑树和ConcurrentHashMap,有待后续的进一步研究。

 

  另外还要提到的是,关于HashMap中的一些扩展点。

1、init()方法:在LinkedHashMap,该方法用于进行虚结点的初始化。

    /**
     * Constructs an empty <tt>HashMap</tt> with the specified initial
     * capacity and load factor.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     * HashMap中的方法
     */
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        this.loadFactor = loadFactor;
        threshold = initialCapacity;
        init();
    }
    /**
     * Called by superclass constructors and pseudoconstructors (clone,
     * readObject) before any entries are inserted into the map.  Initializes
     * the chain.
     * LinkHashMap中重写之后的方法
     */
    @Override
    void init() {
        header = new Entry<>(-1, null, null, null);
        header.before = header.after = header;
    }

 

 2、Entry的recordAccess方法:HashMap中是空实现,从名字可以看出“记录访问”,在LinkedHashMap中进行了重写,用于根据插入或者访问元素的顺序来调整链表元素。

        /**
         * 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.
         * HashMap中Entry的方法
         */
        void recordAccess(HashMap<K,V> m) {
        }
        
        // 以下的是LinkedHashMap中Entry的方法
        /**
         * Removes this entry from the linked list.
         */
        private void remove() {
            before.after = after;
            after.before = before;
        }

        /**
         * Inserts this entry before the specified existing entry in the list.
         */
        private void addBefore(Entry<K,V> existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }

        /**
         * This method is invoked by the superclass whenever the value
         * of a pre-existing entry is read by Map.get or modified by Map.set.
         * If the enclosing Map is access-ordered, it moves the entry
         * to the end of the list; otherwise, it does nothing.
         */
        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }

 

  另外还有几个点,如get(Object key)、addEntry(int hash, K key, V value, int bucketIndex)、createEntry(int hash, K key, V value, int bucketIndex)等,在LinkedHashMap中都进行了重写,用于支持根据插入或者访问元素的顺序来调整链表元素,LinkedHashMap相关的控制变量源码如下,这里大致说明下操作的顺序:

  1. 所有的插入元素的操作都是调用:put(K key, V value)
  2. 如果key已经存在,表明需要更改对应的value,此时不会增加元素,更改value时,都会调用Entry.recordAccess方法
  3. recordAccess中,会根据accessOrder调整访问的元素,当accessOrder = true时,表示按照访问顺序调整,把刚才访问到的元素挪到链表最后,若accessOrder = false,则按照插入元素的顺序,由于不涉及到插入元素,因此不进行操作
  4. 如果key不存在,则都会调用addEntry方法,这个时候,元素都是在链表最后,因此不用进行额外的操作。
  5. addEntry中还调用了removeEldestEntry,用于判断是否要移除最后访问或者插入的元素。这个是实现LRU的一个点。

相关具体说明后续在介绍LinkedHashMap时会进一步细讲。

    /**
     * The iteration ordering method for this linked hash map: <tt>true</tt>
     * for access-order, <tt>false</tt> for insertion-order.
     *
     * @serial
     */
    private final boolean accessOrder;        //true表示按照访问顺序,默认false表示按照插入顺序,调整特定元素至尾部

 

以上是关于Java7/8集合框架——HashMap的主要内容,如果未能解决你的问题,请参考以下文章

Java7/8 HashMap ConcurrentHashMap

Java7/8中的HashMap和ConcurrentHashMap全解析

Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析

Java并发指南13:Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析

解析 Java7/8 中的 HashMap 和 ConcurrentHashMap

Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析(上)