SiYu

积少成多 聚沙成塔

欢迎来到我的个人站~


HashMap

  

构造函数

  HashMap有4个构造函数.分别为:

/**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY) {
            initialCapacity = MAXIMUM_CAPACITY;
        } else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) {
            initialCapacity = DEFAULT_INITIAL_CAPACITY;
        }

        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        // Android-Note: We always use the default load factor of 0.75f.

        // This might appear wrong but it's just awkward design. We always call
        // inflateTable() when table == EMPTY_TABLE. That method will take "threshold"
        // to mean "capacity" and then replace it with the real threshold (i.e, multiplied with
        // the load factor).
        threshold = initialCapacity;
        init();
    }

 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);
    }

  前两种构造方法调用的都是第3个构造方法。这里2个参数 DEFAULT_INITIAL_CAPACITY和DEFAULT_LOAD_FACTOR.


static final int DEFAULT_INITIAL_CAPACITY = 4;
static final float DEFAULT_LOAD_FACTOR = 0.75f;

  DEFAULT_INITIAL_CAPACITY为默认初始容量,默认值为4,必须为2的幂数。这里有一个问题:第一个构造方法public HashMap()注释中说,构造一个空的HashMap,默认初始容量为16,默认构造因子为0.75。 但是这个值默认的4。

  在public HashMap(int initialCapacity, float loadFactor)中会判断initialCapacity的大小,范围为4到1 « 30。 最后将initialCapacity赋给threshold。这个threshold是大小是capacity * load factor,为什么是这个呢,因为在调用put(Key, value)方法时会初始化tab(HashMapEntry<K,V>[]),初始化inflateTable(threshold)时,将threshold赋值为capacity * loadFactor;同时初始化table = = new HashMapEntry[capacity];table的大小为2的幂数。

####HashMapEntry HashMapEntry实现了接口Map.Entry.它的构造方法如下,4个参数分别为hash值,Key,Value,还有一个HashMapEntry<K,V> next,这就是一个链表结构。理想状态下是单链表,但是有时候数据太多时,有可能形成循环链表。


     /**
         * Creates new entry.
         */
        HashMapEntry(int h, K k, V v, HashMapEntry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

####put(Key,value) 方法如下:


 public V put(K key, V value) {
     //如果tab为空,就初始化
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        //如果key为空,就将value保存在tab[0]位置
        if (key == null)
            return putForNullKey(value);
        //计算key的hash值
        int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
        //获取索引i
        int i = indexFor(hash, table.length);
        //获取tab[i]处的HashMapEntry,循环这条链上的HashMapEntry
        //进行遍历
        for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //如果hash值相等,同时key也相等。就替换新值,返回旧值;
            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
        addEntry(hash, key, value, i);//在索引i处添加Entry
        return null;
    }

#####putForNullKey   1.先判断table是否为空,为空就初始化table;2.如果key为null, 返回putForNullKey(value)


private V putForNullKey(V value) {
        for (HashMapEntry<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;
    }

#####addEntry(int hash, K key, V value, int bucketIndex)   方法如下:


void addEntry(int hash, K key, V value, int bucketIndex) {
		//threshold这个值是capacity * loadFactor,例如(16*0.75)
        //如果HashMap的size大于threshold这个值是capacity,
        //同时null != table[bucketIndex],此时需要扩容(变为原来的2倍)
        //在resize()方法中,扩容时,创建一个新的table,
        //同时把旧的table中的所有Entry重新计算hash
        //获得新的index,再放入新的table中
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
		//创建新的Entry
        createEntry(hash, key, value, bucketIndex);
    }

#####createEntry   方法如下:在创建过程中,就是把新值放在索引bucketIndex处,把新值的next指向原来bucketIndex的值,在bucketIndex处就形成了一条单向链.


void createEntry(int hash, K key, V value, int bucketIndex) {
		//将bucketIndex处的Entry赋值给e,
        //创建新的Entry(key,value为新值),同时新的Entry.next指向e;
        //将新的Entry放在table[bucketIndex]处,
        HashMapEntry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
        //HashMap的size+1
        size++;
    }

  以上就实现了HashMap的put过程。

####get(Object key)过程   取值的过程很简单,如果key==null,就遍历table[0]处的链表,获取value;如果key!=null,先计算key的hash值,获取hash&(table.lenth-1)的值index,遍历table[index]处的链表,获取value。


  public V get(Object key) {
  		//key == null,取空Key的值
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }
    
    private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        //遍历index=0处的链表,如果key == null,就返回e.value
        for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }
    
    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }
		//计算key的hash值
        int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
        //遍历indexFor(hash, table.length)处的链表,获取e.value
        for (HashMapEntry<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;
    }

####remove(Object key)过程   移除的过程相对于插入的过程就稍微有点复杂。因为需要在一条链表上移除一个Entry,过程也很简单。获取链表表头,也就是key对应索引index处table[index],从表头向下遍历,获取当前的Entry和Entry.next;还有Entry的表头。如果当前Entry对应key,则将Entry的表头.next指向Entry.next。


	public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        return (e == null ? null : e.getValue());
    }
    
    final Entry<K,V> removeEntryForKey(Object key) {
        if (size == 0) {
            return null;
        }
        //获取key对应的hash值
        int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
        //获取index
        int i = indexFor(hash, table.length);
        HashMapEntry<K,V> prev = table[i];
        HashMapEntry<K,V> e = prev;

        while (e != null) {
            HashMapEntry<K,V> next = e.next;
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {
                modCount++;
                size--;
                //pre为表头
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }

        return e;
    }