Set接口学习小结(草稿)

Set接口学习小结

Set接口概述
​ (1)Set接口是Collection接口的子接口,集合中的元素是无序且不可重复
​ (2)可以存放null值
​ (3)Set接口的常用实现类有:HashSet、LinkedHashSet、TreeSet

一、HashSet

遗留问题:为什么hashset每次添加成功后,table里面的值都会实时改变,在哪里赋的值呢???有没有大佬帮忙解答一下

HashSet是基于哈希表的实现(内部实际就是一个HashMap)
在这里插入图片描述
HashSet特点:
​ 1、hashSet是无序的,无序指的是数据取出的顺序和添加进去的顺序无关,但是在内部按照自己的规则存放
​ 2、hashSet不会有重复的元素,可以添加null值,重复添加数据会返回false
在这里插入图片描述
hashSet底层是数组+链表(单向)+ 红黑树

1、构造方法

在这里插入图片描述

  • HashSet() :构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)
  • HashSet(Collection<? extends E> c) :构造一个包含指定集合中的元素的新集合。
  • HashSet(int initialCapacity) : 构造一个新的空集合; 背景HashMap实例具有指定的初始容量和默认负载因子(0.75)。
  • HashSet(int initialCapacity, float loadFactor) :构造一个新的空集合; 背景HashMap实例具有指定的初始容量和指定的负载因子。
    注意:负载因子是HashSet扩容的关键参数,每次扩容大小取决与初始容量*负载因子
2、HashSet扩容源码分析
  • HashSet的底层是HashMap,HashMap的底层实现是数组+链表+红黑树
  • HashSet的去重机制是通过比较HashCode()和equals(),此处的hashCode和哈希值不一样
  • HashSet当数组长度超过64且链表深度超过8才会树化
    hashSet的结构分析
    在这里插入图片描述
    (1)源码可以看出,HashSet实际就是调用HashMap的构造方法,添加元素时也是把key作为HashMap的key,其值存入的为一个空对象
    在这里插入图片描述
    在这里插入图片描述
    (2)添加元素进来时,会根据传入元素的值调用hash(),这里可以看出HashSet的HashCode()和哈希值不是同一个,哈希值是通过hashCode方法加上计算出的哈希值再右移16位计算出来的
    在这里插入图片描述
    在这里插入图片描述
    (3)通过断点调试发现,首次添加元素,会调用resize(),会初始将table扩容为16个空间大小,扩容的阈值为16*0.75(负载因子)计算出下次要扩容的临界值12。由于是首次添加,将元素添加到集合中
    在这里插入图片描述
    首次扩容方法
    在这里插入图片描述
    在这里插入图片描述
    再次添加时,如果被添加元素的哈希值和集合中的元素存在相同的哈希值,并且被添加元素的值和集合中相同,则返回集合中已存在的值,则会添加失败,这里可以理解为啥都要重写hash值和equals去比较两个元素是否相同
    在这里插入图片描述
    再次添加时,循环判断该链表上的后续元素是不是为空,如果不为null,再判断链表的深度是否超过8;如果超过8,再次调用resize()方法进行判断table数组的长度是否超过64,超过64则进行树化(待补充),否则,继续进行table数组的扩容。此处断点可以看出真正树化操作是在数组长度大于64且链表深度超过8两个条件都满足时才进行树化
    在这里插入图片描述
    逻辑示意图如下
    在这里插入图片描述
    注意:每次成功添加元素后的size都会加一,这里作用是为了记录集合长度到阈值后,下次就会直接将集合扩容
    在这里插入图片描述
    在到达阈值后再次添加元素,直接扩容
    在这里插入图片描述

二、LinkedHashSet

LinkedHashSet是Set中唯一一个有序的集合,其底层是LinkedHashMap,通过数组+双向链表+红黑树实现。
LinkedHashSet特点:

  • LinkedHashSet中没有重复元素
  • LinkedHashSet中维护了元素的添加顺序,所以是有序的
1、构造方法

在这里插入图片描述

  • LinkedHashSet() 构造一个具有默认初始容量(16)和负载因子(0.75)的新的,空的链接散列集。
  • LinkedHashSet(Collection<? extends E> c) 构造与指定集合相同的元素的新的链接散列集。
  • LinkedHashSet(int initialCapacity) 构造一个具有指定初始容量和默认负载因子(0.75)的新的,空的链接散列集。
  • LinkedHashSet(int initialCapacity, float loadFactor) 构造具有指定的初始容量和负载因子的新的,空的链接散列集。
2、LinkedHashSet扩容源码分析

​ LinkedHashSet的底层是LinkedHashMap,LinkedHashSet继承了HashSet,扩容机制和HashSet类似;与HashSet不同的是,LinkedHashSet是有序的,内部是通过双向链表来确定元素的顺序。
​ LinkedHashSet添加元素扩容和HashSet类似,不在赘述,下面仅仅分析差异点。
​ ① LinkedHashSet中多了head和tail两个属性记录集合的头和尾节点
​ ② LinkedHashSet的table中存储的元素类型为LinkedHashMap$Entry,而HashSet中存储为Node类型,LinkedHashSet的table中的Entry中通过before和after记录元素的顺序
​ 差异点①
在这里插入图片描述
​ 差异点②,源码可以看出Entry继承了HashMap.Node这个静态内部类实现,但是LinkedHashMap多了before和after去记录元素间的顺序

/**
 * HashMap.Node subclass for normal LinkedHashMap entries.
 */
static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

添加元素时,LinkedHashMap的重写newNode方法,创建前后两个元素的关联关系

private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
    LinkedHashMap.Entry<K,V> last = tail;
    tail = p;
    // 判断集合的尾节点元素是否为空,
    if (last == null)
	    // 为空则将head也指向被创建的元素,
        head = p;
    else {
    	// 不为空,则通过before指向上一个元素,上一个元素的last指向该元素
        p.before = last;
        last.after = p;
    }
}

如果集合中元素的hash值不一样,只会记录元素的before和after的关联关系
在这里插入图片描述
如果两个元素的hash值一样,也会通过next记录在同一个hash值的元素信息
在这里插入图片描述
由此可以发现,next字段记录的是在同一个hash值上,也就是table的同一个位置,链表上存在不同元素的关联关系

三、TreeSet

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值