teiba

导航

HashMap源码解析

变量:

    /**
     * 初始化容量为16
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    /**
     * 最大容量:2^30
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 负载因子默认0.75,负载因子越小,hash冲突几率越低
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * 初始化一个空的数组
     */
    static final Entry<?,?>[] EMPTY_TABLE = {};

    /**
     * 存值的数组,初始化为空数组
     */
    transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

    /**
     * HashMap存入元素的个数
     */
    transient int size;

    /**
     * 临界值,HashMap能存储的大小,公式为(threshold=capacity*loadFactor)
     */
    // If table == EMPTY_TABLE then this is the initial capacity at which the
    // table will be created when inflated.
    int threshold;

    /**
     * 负载因子
     */
    final float loadFactor;

    /**
     *HashMap修改的次数,用于迭代
     */
    transient int modCount;

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

 

构造方法:

    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)//MAXIMUM_CAPACITY 最大容量
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))//loadFactor 负载因子
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        this.loadFactor = loadFactor;
        threshold = initialCapacity;
        init();
    }

走完构造方法,HashMap创建了一个空的数组,transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE; 当使用put(K, V)存入键值对的时候,才会使用inflateTable(initialCapacity)初始化数组的大小为initialCapacity。下面是put(K, V)源码:

    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);//初始化table的大小
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);//根据key值计算出hash值
        int i = indexFor(hash, table.length);//根据hash值计算出数组的下标
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {//如果当前数组上的链表头不为null,遍历链表
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//不为null时,检测key值得hash是否相同,如果相同替换掉原来的值,并返回原来的值
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;//修改次数+1
        addEntry(hash, key, value, i);//添加元素
        return null;
    }
    private static int roundUpToPowerOf2(int number) {
        // assert number >= 0 : "number must be non-negative";
        return number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;//确保是2的倍数
    }

    /**
     * Inflates the table.
     */
    private void inflateTable(int toSize) {
        // Find a power of 2 >= toSize
        int capacity = roundUpToPowerOf2(toSize);

        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);//临界值赋值
        table = new Entry[capacity];
        initHashSeedAsNeeded(capacity);
    }

放入元素,如果是table是空数组,初始化table,临界值;

key是null,将该entry放入到数组table的第0个下标处(第一个位置);

根据key值计算出hash值,根据hash值计算出在数组table中的下标,根据下标找到链表,如果链表头不为null,遍历链表,检查key值是否存在于链表,如果存在,将新值替换掉旧值,并返回旧值;

addEntry方法,将新的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);//表头引用该变成要插入的entry元素
        if (size++ >= threshold)
            resize(2 * table.length);//加入元素后,空间不足,扩容table长度的2倍
    }

 

posted on 2018-05-25 11:28  teiba  阅读(218)  评论(0编辑  收藏  举报