Java集合自学----Set接口之HashSet

HashSet
在初学集合时,误以为Set和Map是两个接口,底层的数据结构也是不一致的,但是在深入源码的学习中才明白Set的底层就是Map。

(1) 实现了Set接口

(2)HashSet实际是HashMap,底层是一个hashmap

(3)可以放一个空值

(4)HashSet不保证元素是有序的,取决于hash之后,在确定索引的结果

(5)不能有重复元素

底层结构分析

		底层结构为:数组+链表+红黑树

		Node数组  也称为表

		初始化为16  Node[]数组   在数组中的存放为链式存储  
会在每一个索引位置形成链表  

源码分析

		1.HashSet底层是hashmap

		2.添加一个元素会先得到hash值,会转成索引表

		3.找到存储表,看索引位置是否有元素

		4.如果没有直接加入

		5.如果有,调用equals进行比较,若相同就会放弃添加,若不相同就放在最后

		6.如果一条链表的元素个数到8,而且索引数组大小大于64,就会转为红黑树

先执行HashSet构造器,底层为hashmap 执行add 中的put方法 key值 value是一个占位符,

public HashSet() {
        this.map = new HashMap();
    }
 public V put(K var1, V var2) {
        return this.putVal(hash(var1), var1, var2, false, true);
    }

求值的hashcode值按位异或并且后移16位防止冲突,但是还是会有hash冲突。

putval(hash(key),key,value)
static final int hash(Object var0) {
        int var1;
        return var0 == null ? 0 : (var1 = var0.hashCode()) ^ var1 >>> 16;
    }

扩容机制分析
table 是hashmap的一个属性 table默认表的大小就是十六,0.75为加载因子,当达到当前容量的0.75倍时就准备扩容。 然后根据传入的key得到的hash值,来判断会存放在那个位置,并把这个位置即表中的对象赋给p,判断是否为空,若p是空表示还没存放数据,就创建一个node(hash,key是值,value是占位符,null为指针),就放在那个位置 。看一下size是否到达临界值,判断是否扩容。返回空代表成功,返回其他的是旧对象。

第一个数据剖析

	1.执行new对象会调用底层的hasmap构造方法,会创建一个空的hasmap集合,得到负载参数为0.75.
public HashSet() {
        this.map = new HashMap();
    }


	2.执行add操作,add中是hasmap中的put方法,执行该方法会将值作为key,并且放入一个占位符作为value,put中的方法为putval(hash(key)计算key的hash值后按位异或并且后移16位防止哈希冲突,key为传入的值,value为占位符) 创建一个16大小的数组。
public boolean add(E var1) {
        return this.map.put(var1, PRESENT) == null;
    }
if ((var6 = this.table) == null || (var8 = var6.length) == 0) {
            var8 = (var6 = this.resize()).length;
        }
 public V put(K var1, V var2) {
        return this.putVal(hash(var1), var1, var2, false, true);
    }
putval(hash(key),key,value)
static final int hash(Object var0) {
        int var1;
        return var0 == null ? 0 : (var1 = var0.hashCode()) ^ var1 >>> 16;
    3.然后判断table即Node数组是否为空,然后根据传入的key得到的hash值,来判断会存放在那个位置,并把这个位置即表中的对象赋给p,判断是否为空,若p是空表示还没存放数据,就创建一个node(hash,key是值,value是占位符,null为指针),就放在那个位置 。

        this.threshold = var5;
        HashMap.Node[] var14 = (HashMap.Node[])(new HashMap.Node[var4]);
        this.table = var14;
   		//在表中则呈现node存放
   		Node(int var1, K var2, V var3, HashMap.Node<K, V> var4) {
            this.hash = var1;
            this.key = var2;
            this.value = var3;
            this.next = var4;
        }
	4.看一下size是否到达临界值为当前数量*负载指数,判断是否扩容。返回空代表成功,返回其他的是旧对象。容量调整按两倍当前的容量来扩充

在这里插入图片描述

//原数值为12
  this.threshold = var5;
   if (++this.size > this.threshold) {
            this.resize();
        }
  if ((var4 = var2 << 1) < 1073741824 && var2 >= 16) {
                var5 = var3 << 1;
            }	
  //此时得到数值为24

第二个数据的加入

	1.按上述进入putval中,得到hash值后,先判断有无hash冲突,然后放入该值,

	2.若值相同,判断当前索引位置的元素的哈希值和该值是否相同,若哈希值相同且该值和索引中的值相同,或者hash值相同且对象相同(equals方法比较),则该元素不能添加到数组中

	最后看是不是红黑树,若是一颗红黑树就按树的方法添加。

    如果也不是红黑树,看这条链表所有元素是否相同,有一个相同就无法添加,都不相同则放在最后面。立即判断是否达到八个节点,然后立即调用树化方法,对当前链表进行树化,在转成红黑树时还进行一个判断,看是否小于64,若小于64则扩充数组大小,即二倍扩容。

扩容方法

		1.在hash值数组到达数组容量*0.75时,就开始扩容

		2.一个索引中的链表长度大于8时,扩容。

知识点补充

在判断索引数组是否达到临界值时,我们只要在数组中添加一个节点,size就会加一,不论是添加到node的链表亦或者是添加到table中。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值