HashMap

好久没有写博客来总结了,这两天准备花点时间写写这几天看的些框架,谈谈自己的理解。

HashMap 使用的理解

HashMap 以前的理解就是一个集合,里面存放着键值对(key-value),然后键不能重复,值可以重复。可以用它来存放 put(Key ,Value),取出get(key)

HashMap 源码理解

我认为学习一个东西最好把他的原理理解好,这样不就是对记忆和使用的有莫大的用处。所以我们就开始看看这神秘的HashMap 吧。

首先,我们知道HashMap 是用来存储数据的,那么我们了解它,就应该从数据结构开始。HashMap 内部采用的是数组+链表的形式进行数据的存储。
内部存储
可以看见 HashMap 中我们首先是有一列数组,然后每个数组又对应了一个Entry的链表(这里的Entry其实就是我们的键值对的包装,当我们获取map的size时,有多少个Entry,size就为多少了)。那我们知道了内部存放情况后就可以开始真正的看源码了。

public HashMap() {
        table = (HashMapEntry<K, V>[]) EMPTY_TABLE;
        threshold = -1}

这里可以看到初始化的时候我们用默认的size创建了一个数组,当然你也可以自己确定数组大小public HashMap(int capacity), public HashMap(int capacity, float loadFactor) 以及它的加载因子。这个数组就是我们开始所说的数组。

那好,现在我们就开始看看是如何存数据进去的,毕竟这才是我们最想了解的。

public V put(K key, V value) {
//判断key是否为null,如果为null则直接调用 putValueForNullKey(value)
        if (key == null) {
            return putValueForNullKey(value);
        }
//生成key的hashcode
        int hash = Collections.secondaryHash(key);
        HashMapEntry<K, V>[] tab = table;
        //把hashcode的值与 (tab.length - 1)经行位运算,然后通过映射找到该Entry应该位于数组的哪一个上
        int index = hash & (tab.length - 1);
        for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
        //链表的查找,如果找到的话,就会用传进来的新的value换掉旧的value 
            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
        //如果在对应的数组元素中没有该key的Entry,就会在链表中添加填一个。
        modCount++;
        if (size++ > threshold) {
            tab = doubleCapacity();
            index = hash & (tab.length - 1);
        }
        addNewEntry(key, value, hash, index);
        return null;
    }

这里我们就主要看看put(key,value),其实主要的我的注释都解释了。不过这里,我想在强调两个地方。一个就是putValueForNullKey(value) 这里比较简单,我也没有贴源码,就是把它添加在数组的第一个元素上。当然,再添加之前他会先查看以前是否又Entry,有的话就会换掉再把旧的返回回去。另一个就是if (e.hash == hash && key.equals(e.key)),在这里,就是我们判断key是否在map中的逻辑了。 不过值得注意的是这里有两个限定条件,一个是hash值,另一个是equals。如果以前有接触的,我们知道java官方推荐我们复写equakls的时候是需要保证返回的hashcode的值是相同的。即在这里,我们是需要hash值相同的key对象才是相同的key。
不过还值得提起的是,就是当我们 put(key,value) 时候,其实是把hash值的高字节和数组长度-1就行位运算,以保证返回的值在数组中。


我想put差不多就是这样,接着我们就看看HashMap是如何取出的吧。

public V get(Object key) {
//首先还是判断key是否为null,如果为null,则返回开始存在0位置的Entry
        if (key == null) {
            HashMapEntry<K, V> e = entryForNullKey;
            return e == null ? null : e.value;
        }
//不为null,则获取hash值,在位运算找到所属的那个数组元素。接着对链表进行遍历,根据hash值寻找,找到的话就返回value,否则返回null。
        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(key,value) ,那么 get(key) 的理解就是很容易的了,同理,remove(key) 等方法也是大同小异的。


总结

HashMap 是可以存放<null,value>的,不过这里存了null,也是只能存一个,再次存null就会覆盖过以前的。HashMap 在根据key生成hash之后,是根据某种算法来选择数组中的元素的。让后的存储和取出则是根据key值是否相同再经行存储或者返回的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值