HashMap源码解析(JAVA 1.6)

<span style="font-size:18px;">本文主要从构造函数、put函数、get函数、底层hash表等4个方面来对HashMap的源码进行解析。前三个都是我们在编程中经常要用到的,最后一个则是对前三个的支撑。
构造函数</span>
<span style="font-size:18px;"><strong>HashMap提供的三个构造函数</strong></span>
/**
     *自定义HashMap的初始容量与负载因子
     * @param  initialCapacity   初始容量
     * @param  loadFactor    负载因子
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    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);

        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;

        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);  //这个参数是HashMap进行扩容的标志,当容量达到threshold时,HashMap便会进行扩容操作。它的值由容量和负载一起决定
        table = new Entry[capacity];
        init();
    }
/**
     * 自定义HashMap的初始容量 
     *
     * @param  initialCapacity 初始容量 
     * @throws IllegalArgumentException if the initial capacity is negative.
     */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR); //默认负责因子为0.75
    }

    /**
     *无参构造函数 默认初始容量为16  默认负责因子为0.75
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        table = new Entry[DEFAULT_INITIAL_CAPACITY];
        init();
    }
/**
     * 根据已有的散列表进行初始化。
     * @throws  NullPointerException if the specified map is null
     */
    public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        putAllForCreate(m);
    }
了解了这些构造函数后,如果在已知将要构造的HashMap大小的情况下,可以用前两种方法,设定初始容量大小,这样可以减少HashMap的扩容次数,毕竟每次扩容都涉及到数组的拷贝,这样可以提升效率。这属于细节问题。
<strong><span style="font-size:18px;">HashMap的put函数</span></strong>
 /**
     *插入K-V键值对
     */
    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode()); //获取主键的HASH值
        int i = indexFor(hash, table.length); //根据主键的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;
            }
        }

        modCount++;
        addEntry(hash, key, value, i); //添加到散列表
        return null;
    }

    /**
     * 添加KEY为NULL的K-V键值对  
     */
    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); //KEY为NULL的键值对,索引永远为0
        return null;
    }

    /**
     * 插入K-V键值对,但不会对扩容进行影响。即不进行扩容判断
     */
    private void putForCreate(K key, V value) {
        int hash = (key == null) ? 0 : hash(key.hashCode());
        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 (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry<? extends K, ? extends V> e = i.next();
            putForCreate(e.getKey(), e.getValue());
        }
    }
<strong><span style="font-size:18px;">HashMap的get函数</span></strong>
/**
     *根据K值获取V值
     */
    public V get(Object key) {
        if (key == null) //判断K是否为NULL
            return getForNullKey();
        int hash = hash(key.hashCode()); //获取其HASH值
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) { //遍布K所在的链表
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }

    /**
     *获取K为NULL的V值
     */
    private V getForNullKey() {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

    /**
     *获取K值 对应的实体类对象(散列对象)
     */
    final Entry<K,V> getEntry(Object key) {
        int hash = (key == null) ? 0 : hash(key.hashCode()); //判断K是否为NULL
        for (Entry<K,V> e = table[indexFor(hash, table.length)]; //遍历K所在的链表
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }
<strong><span style="font-size:18px;">HashMap的底层hash表</span></strong>
/**
     * 默认容量 -必须为2的幂次方.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 16;
    /**
     * 最大容量为2的30次方
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;
    /**
     * 默认负载值
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    /**
     *散列表
     */
    transient Entry[] table;
HashMap的底层存的不是一个简单的数组,而是一个链表数组。如图1所示:
<img src="https://img-blog.csdn.net/20160110202905335?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
                                                       图1  HashMap底层存储结构
从横向来看,它是一个数组,从纵向来看它是一个链表。在HashMap里面是用一个 Entry[] table 来存储这个链表数组的。那么它又是怎样对这个链表数组进行操作的呢?
这里以向HashMap中插入数组为例:
 /**
     *插入K-V键值对
     */
    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode()); //获取主键的HASH值
        int i = indexFor(hash, table.length); //根据主键的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;
            }
        }

        modCount++;
        addEntry(hash, key, value, i); //添加到散列表
        return null;
    }

主要分为4步:
第一步 对Key进行判断,看是否为NULL 如果为NULL 则直接可得到HASH值为0 
第二步 如果非NULL 则对Key进行HASH,得到对应的hash(Key)
第三步 根据IndexFor()方法,用hash(Key)与length-1进行位与运算,得到当前hash(Key)在散列表中对应的索引,即横向索引值。IndexFor()方法源码如下:
 /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }
第四步 在对应的链表中查询,看是否已经存在对应的KEY,如果存在,则直接替换,如果不存在,则在创建一个Entry对象,同时将其添加到hash(Key)对应的链表的头部。
运用数组进行快速索引,同时利用链表去解决hash冲突。最新插入的在最前。
/**
     * 在链表中添加Entry对象
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
 Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }
/**
         *Entry 对象构造 方法
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

声明:本文系原创 转载请注明 谢谢!

    原文作者:二进制的蛇
    原文地址: https://blog.csdn.net/lza1989/article/details/50493583
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞