HashMap源码学习

* @see HashMap
* @see TreeMap
* @see Hashtable
* @see SortedMap
* @see Collection
* @see Set

HashMap继承自AbstractMap类,AbstractMap类实现Map接口。

一、HashMap中的常量(此处我的JDK是1.8):

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16    

   默认初始化的HashMap容量大小,即2^4 = 16,初始化一个容量大小为16的HashMap。

static final int MAXIMUM_CAPACITY = 1 << 30;

 HashMap最大的容量,即2^30 = 1073741824,HashMap最大容量为这个,如果超过该大小,则以该大小为当前HashMap容量。

static final float DEFAULT_LOAD_FACTOR = 0.75f; 

HashMap的默认负载因子,即实际最大容量 / 初始最大容量 的比值,当实际最大容量超过 初始最大容量*负载因子 时,HashMap则进行扩容。

static final int TREEIFY_THRESHOLD = 8;

HashMap中解决hash冲突的链表长度超过8时,发生hash冲突的数据数据结构会由链表变成树

static final int UNTREEIFY_THRESHOLD = 6;

HashMap在扩容时,如果发现树节点少于6个时,HashMap中的树结构会退回链表结构

static final int MIN_TREEIFY_CAPACITY = 64;

HashMap中链表转树结构前的HashMap中键值对的最小数量,键值对数量不超过 64个,则不进行链表转树操作,MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的值的4倍。

 

二、HashMap中的几个全局变量介绍:

/**
     * The table, initialized on first use, and resized as
     * necessary. When allocated, length is always a power of two.
     * (We also tolerate length zero in some operations to allow
     * bootstrapping mechanics that are currently not needed.)
     */
    transient Node<K,V>[] table;

节点数组,因为HashMap的结构是有数组和链表(红黑树),所以table是用来存放key-value键值对的数组。

/**
     * Holds cached entrySet(). Note that AbstractMap fields are used
     * for keySet() and values().
     */
    transient Set<Map.Entry<K,V>> entrySet;

键值对的Set集合,HashMap的另一种数据存储方式,该变量存放的是EntrySet的实例。EntrySet是HashMap的一个内部类(后面会讲到),主要的用处是对HashMap进行迭代操作。该变量通过HashMap的entrySet()方法进行初始化。

/**
     * The number of key-value mappings contained in this map.
     */
    transient int size;

    HashMap中key-value键值对的实际数量,这个变量的值 和 HashMap中key-value键值对的数组table长度不一定相同。因为HashMap中可能包含链表或红黑树,所以,这个变量的值可能会大于table数组的长度。

/**
     * The number of times this HashMap has been structurally modified
     * Structural modifications are those that change the number of mappings in
     * the HashMap or otherwise modify its internal structure (e.g.,
     * rehash).  This field is used to make iterators on Collection-views of
     * the HashMap fail-fast.  (See ConcurrentModificationException).
     */
    transient int modCount;

       该变量代表HashMap被修改的次数,这个变量用于迭代过程中的Fail-Fast机制,如果在某个线程操作HashMap时发现此线程备份的modCount和当前的modCount大小不相同时,可以快速抛出异常(ConcurrentModificationException)并终止操作,但是快速失败策略不能保证线程安全。

/**
     * The next size value at which to resize (capacity * load factor).
     *
     * @serial
     */
    // (The javadoc description is true upon serialization.
    // Additionally, if the table array has not been allocated, this
    // field holds the initial array capacity, or zero signifying
    // DEFAULT_INITIAL_CAPACITY.)
    int threshold;

    HashMap进行扩容的阈值大小,该变量的大小等于HashMap容量*负载因子,即:threshold = capacity * load factor。

/**
     * The load factor for the hash table.
     *
     * @serial
     */
    final float loadFactor;

负载因子,即HashMap进行扩容时的阈值和容量的比值。

三、HashMap中的内部类介绍:

1、Node<K,V> 类:

/**
     * Basic hash bin node, used for most entries.  (See below for
     * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
     */
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

Node<K,V> 类实现 Map.Entry<K,V>接口,参数有四个:

   final int hash;                       //hash值
   final K key;                          //键值对的key值
   V value;                              //键值对的value值
   Node<K,V> next;               //下一个Node节点的值
    提供的方法:一个有参的构造方法、key和value的get方法、value的set方法、toString方法、以及重写的equals和hashCode方法。其中setValue方法在set成功后会把value的老值当做结果返回。

2、KeySet 类:

final class KeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<K> iterator()     { return new KeyIterator(); }
        public final boolean contains(Object o) { return containsKey(o); }
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null;
        }
        public final Spliterator<K> spliterator() {
            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super K> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.key);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }

该内部类继承自AbstractSet,AbstractSet是Set的实现类。所以KeySet具有Set的所有特性。顾名思义,该Set集合是存放着HashMap键值对中key值的集合。

该内部类提供的方法有:

①、size()方法

public final int size()                 { return size; }

 返回 HashMap中key-value键值对的实际数量,即HashMap的大小,也是KeySet的大小。

②、clear()方法

public final void clear()               { HashMap.this.clear(); }

调用HashMap的clear方法,清空HashMap,以达到清空KeySet的目的。我们看下HashMap中的clear方法源码:

/**
     * Removes all of the mappings from this map.
     * The map will be empty after this call returns.
     */
    public void clear() {
        Node<K,V>[] tab;        //定义一个Node数组tab
        modCount++;            //让HashMap的修改次数做一次自增
        if ((tab = table) != null && size > 0) {   //将HashMap的Node数组table的引用赋给tab,
                                                   //判断tab是否为空,并且                                            
                                                   //HashMap中键值对数量是否大于0
            //如果条件满足
            size = 0;                              //让HashMap中的size等于0
            for (int i = 0; i < tab.length; ++i)   //遍历HashMap的Node数组,
                tab[i] = null;                     //让HashMap中每一个Node元素的值都为null,
        }
    }

③、iterator()方法

public final Iterator<K> iterator()     { return new KeyIterator(); }

该方法返回KeyIterator类的实例,KeyIterator继承HashIterator类,实现Iterator接口,我们看一下内部类KeyIterator及与其相似的ValueIterator类和EntryIteraror类的源码:

final class KeyIterator extends HashIterator
        implements Iterator<K> {     //对HashMap中的Key值进行循环操作的Iterator
        public final K next() { return nextNode().key; }
    }

final class ValueIterator extends HashIterator
        implements Iterator<V> {    //对HashMap中的value值进行循环操作的Iterator
        public final V next() { return nextNode().value; }
    }

final class EntryIterator extends HashIterator
        implements Iterator<Map.Entry<K,V>> {  //对Map中的Entry键值对进行循环操作的Iterator
        public final Map.Entry<K,V> next() { return nextNode(); }
    }

我们发现这三个类都只有一个final修饰的next()方法,而方法体内都调用了nextNode()方法,这里的nextNode()方法是它们父类HashIterator类的方法,我们看下它们父类HashIterator的源码,以及nextNode()方法的实现。

// iterators
//HashMap中的Node是继承自Map的Entry类的,所以我们这里就把Node和Entry归为一类了。
    abstract class HashIterator {
        Node<K,V> next;        // next entry to return    下一个Node键值对
        Node<K,V> current;     // current entry           当前的Node键值对
        int expectedModCount;  // for fast-fail           期望的HashMap被修改次数,
                                                          //在快速-失败策略上使用的
        int index;             // current slot            //当前的位置索引

        HashIterator() {                      //无参构造器
            expectedModCount = modCount;      //将当前HashMap的修改次数赋给expectedModCount
            Node<K,V>[] t = table;            //将HashMap的当前Node数组赋值t
            current = next = null;            //初始化current和next都为null
            index = 0;                        //初始化当前索引为0

            if (t != null && size > 0) { // advance to first entry
                do {} while (index < t.length && (next = t[index++]) == null);
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Node<K,V> nextNode() {
            Node<K,V>[] t;
            Node<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            if ((next = (current = e).next) == null && (t = table) != null) {
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }

        public final void remove() {
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;
            K key = p.key;
            removeNode(hash(key), key, null, false, false);
            expectedModCount = modCount;
        }
    }

未完待续~~~2018-12-15

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值