HashMap源码分析

当我们需要存储key-value这样格式的数据的时候,我们通常会使用Map,实现Map接口的类有我经常使用的是HashMap,所以简单介绍下HashMap的源码部分。。。。。。。

 

1.       HashMap的存储结构

HashMap的数据结构包括了数组与链表,他首先将每一对key-value数据存储为一个Entry类型(Entry内部包括key,value,一个int型的hash,一个next对象,类似指针),然后将他们存储到数组的对应位置,数组的每个位置对应一个链表,链表内存储Entry类型(每个Entry都有一个next,所以类似链表,下文暂且这么叫)。

如图:

 

2.       首先看下hashMap的基本变量们:

 

private static final int MINIMUM_CAPACITY = 4; //map的最小容量
private static final int MAXIMUM_CAPACITY = 1 << 30;//map的最大容量 一定要是2的n次方
static final float DEFAULT_LOAD_FACTOR = .75F; //加载因子 容量不够的时候扩容用的
transient HashMapEntry<K, V>[] table;//就是上文中说的数组

 

3.       构造函数


取最常用的介绍:


public HashMap(int capacity) {
    if (capacity < 0) {
        throw new IllegalArgumentException("Capacity: " + capacity);
    }

    if (capacity == 0) {
        @SuppressWarnings("unchecked")
        HashMapEntry<K, V>[] tab = (HashMapEntry<K, V>[]) EMPTY_TABLE;
        table = tab;
        threshold = -1; // Forces first put() to replace EMPTY_TABLE
        return;
    }

    if (capacity < MINIMUM_CAPACITY) {
        capacity = MINIMUM_CAPACITY;
    } else if (capacity > MAXIMUM_CAPACITY) {
        capacity = MAXIMUM_CAPACITY;
    } else {
        capacity = Collections.roundUpToPowerOfTwo(capacity);
    }
    makeTable(capacity);
}

 

capacity是指Map的大小,上面的码码指定了上文说到的数组的大小,进入最后的方法:


private HashMapEntry<K, V>[] makeTable(int newCapacity) {
     HashMapEntry<K, V>[] newTable
            = (HashMapEntry<K, V>[]) new HashMapEntry[newCapacity];
    table = newTable;
    threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity
    return newTable;
}

新建了一个数组叫table(上文的数组叫table),也就是HashMap中主要的数据存储部分,threshold是指允许数据最多的时候的数量。

 

4.      常用方法


a.       put


public V put(K key, V value) {
    if (key == null) {
        return putValueForNullKey(value);
    }

    int hash = Collections.secondaryHash(key);
    HashMapEntry<K, V>[] tab = table;
    int index = hash & (tab.length - 1);
    for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
        if (e.hash == hash && key.equals(e.key)) {
            preModify(e);
            V oldValue = e.value;
            e.value = value;
            return oldValue;
        }
    }

    // No entry for (non-null) key is present; create one
    modCount++;
    if (size++ > threshold) {
        tab = doubleCapacity();
        index = hash & (tab.length - 1);
    }
    addNewEntry(key, value, hash, index);
    return null;
}

            当添加了一个key为null时,调用putValueForNullKey(value)方法,将这个对象添加到table[0]中。正常添加数据时,根据找到key对应的hash值,然后将获得的hash值与HashMap的长度-1进行与运算(HashMap的长度为2的n次方的好处就在这一步体现,充分节约数据空间也减少了冲突),获得的数值就是这个Entry英才存储的数组的位置,即table[index],然后找到数组的index位置上的链表(通过Entry内部的next不断遍历),并将Entry添加到链表最后一位,那个if判断是当内部存在hash和key都与将添加的key一致的情况时,返回原来的key对应的value值,然后替换成新加入的value值,put完成啦~


b.      get


public V get(Object key) {
    if (key == null) {
        HashMapEntry<K, V> e =entryForNullKey;
        return e == null ? null :e.value;
    }
    int hash =Collections.secondaryHash(key);
    HashMapEntry<K, V>[] tab =table;
    for (HashMapEntry<K, V> e =tab[hash & (tab.length - 1)];
            e != null; e = e.next) {
       K eKey = e.key;
        if (eKey == key || (e.hash ==hash && key.equals(eKey))) {
            return e.value;
        }
    }
    return null;
}

与put操作相反,为null就在table[0]中寻找,如果不为空,根据key计算hash值,然后将hash值和HashMap的长度减1相与,遍历链表找到hash 与key和传入的Key对应的相同的Entry,然后取出其中的value值。

 

简直匆忙,理解尚浅,日后修补其他方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值