HashSet/HashMap/add()/put()源码详解

HashSet/HashMap简介

  • hashset是一个单列集合,但是做到了元素的无序和唯一,其实底层原理为hashmap,将key-value中的value替换成一个静态常量,key为添加入集合的元素。
  • HashSet存放数据的结构和HashMap一样,为 数组+链表+红黑树 。下图中还未树化,然后Entry就是Node,只是jdk8以后hashmap中Entry改叫Node了哈哈哈哈。。

  •  HashMap是一个双列集合,做到了元素的无序和唯一

  •  HashSet add()方法debug   HashMap putVal()方法debug

  •  add()方法

         map字段 以及hashset无参构造方法

private transient HashMap<E,Object> map;
public HashSet() {
        map = new HashMap<>();
    }

      hashset类中的add方法

public boolean add(E e) {
        return map.put(e, PRESENT)==null;  //return true表示添加成功
    }

//PRESENT为hashset中的static成员常量,实质指向堆内存中一块空间
private static final Object PRESENT = new Object();

     map字段指向hashmap实例,调用hashmap的put方法 

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

     讲到这里就不得不先引入Object超类中的hashCode方法了,hashCode() 由c++实现,拿到对象的地址(对于HashSet中的元素来说就是key的地址),通过一定的算法解析出hashcode码。值得注意的是hashcode码为int类型,也就是说hashcode支持 -2^32~2^32-1 量级的整型数据范围,共4个字节,32位。

 @IntrinsicCandidate
    public native int hashCode();

    然后再回看putVal中的参数列表第一个形参hash(key),hash()方法是hashmap类中的一个方法,目的是为了解决直接通过hashcode得到数组索引位易出现高哈希冲突频率的,先上方法代码:

 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 }

    可以看到,hash()方法在传入的key有实际指向后return一个h = key.hashCode()) ^ (h >>> 16)

其本质就是由该表达式替换了原先的hashCode方法直接得到的hashcode码,那么为啥要这样呢,可以移步我的另一个回答,上链接:

(h = key.hashCode()) ^ (h >>> 16)_要转码成功呀的博客-CSDN博客

  •  putVal()方法

    继续,解答完hash(key)后,我们就来到了最主要的添加元素方法主体,putVal方法体:

 onlyIfAbsent – if true, don't change existing value
 evict – if false, the table is in creation mode.

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict)

    putVal方法的代码较多,我把它分成三部分来说

  •     第一部分:对hashset初始化,扩容机制判断,元素放置索引位的计算

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        //定义了辅助变量 空的tab数组  索引节点p 数组扩容后的长度n 索引编号i
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //table:  transient Node<K,V>[] table;//成员字段table
        //如果tab为空或者tab长度为0就进行第一次扩容
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length; //n=16  tab=table=new Node<K,V>[16]
        //根据传入的key,得到hash 去计算要存放在tab中的索引,判断索引位置是否为空,
        //为空则添加new Node<K,V>元素
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

    怕有人看不懂newNode怎么蹦出来的,上源代码:

Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
        return new Node<>(hash, key, value, next);
    }

     就是一个Node对象而已哈哈哈哈。。。。 

     这里有必要说明一下,table数组是hashmap中的一个Node<K,V&g

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值