Java中的HashSet、LinkedHashSet集合解析
作者:追光而遇
这篇文章主要介绍了Java中的HashSet、LinkedHashSet集合解析,与HashSet不同的是,LinkedHashSet在内部使用了一个双向链表来维护元素的顺序,因此它可以保持元素的插入顺序,这使得LinkedHashSet在需要保持元素顺序的场景下非常有用,需要的朋友可以参考下
Set系列集合
- List系列集合:添加的元素是有序、可重复、有索引
- Set系列集合:添加的元素是无序、不可重复、无索引
- 无序:存取顺序不一致
- 不重复:可以去除重复
- 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
Set集合的实现类
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TreeSet:可排序、不重复、无索引
Set接口中的方法上基本上与Collection的API一致
Collection
Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的
方法名称 | 说明 |
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数/集合长度 |
练习:存储字符串并遍历
利用Set系列的集合,添加字符串,并使用多种方式遍历
1。迭代器
2。增强for
3。Lambda表达式
public class A01_SetDemo1 { public static void main(String[] args) { //1.创建一个Set集合的对象 Set<String> s = new HashSet<>(); //2.添加元素 boolean r1 = s.add("朵朵"); boolean r2 = s.add("朵朵"); //true System.out.println(r1); //false System.out.println(r2); //[朵朵] System.out.println(s); boolean r3 = s.add("小七"); boolean r4 = s.add("钢镚"); //无序 //[钢镚, 朵朵, 小七] System.out.println(s); //迭代器遍历 Iterator<String> it = s.iterator(); while (it.hasNext()) { String str = it.next(); System.out.println(str); } //增强for for (String str : s) { System.out.println(str); } //Lambda s.forEach(str -> System.out.println(str)); } }
HashSet
- HashSet集合底层采用哈希表存储数据
- 哈希表是一种对与增删改查数据性能都较好的结构
哈希表组成
- JDK8之前:数组+链表
- JDK8之后:数组+链表+红黑树
哈希值
对象的整数表现形式
- 根据hashCode方法算出来的int类型的整数
- 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
- 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
对象的哈希值特点
- 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
- 如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
- 在小部分情况下,不同的属性值或者不同地址值计算出来的哈希值也可能一样(哈希碰撞)
public class A02_HashSetDemo1 { public static void main(String[] args) { //1。创建对象 Student s1 = new Student("朵朵", 5); Student s2 = new Student("朵朵", 5); //2。 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的 //1554874502 System.out.println(s1.hashCode()); //1846274136 System.out.println(s2.hashCode()); //重写hashCode后:不同的对象只要属性值相同,计算出的哈希值就是一样的 //26209637 System.out.println(s1.hashCode()); //26209637 System.out.println(s2.hashCode()); //3。在小部分情况下,不同的属性值或者不同地址值计算出来的哈希值也可能一样(哈希碰撞) //96354 System.out.println("abc".hashCode()); //96354 System.out.println("acD".hashCode()); } }
HashSet的底层原理
1.创建一个默认长度16,默认加载因子0.75的数组,数组名table
- 加载因子:扩容时机
- 当存入数据达到16 x 0.75 = 12时候,数组扩容成原数组的二倍
- JKD8以后:当链表长度大于8而且数组长度大于等于64,当前链表自动转成红黑树,增加查找效率
2.根据元素的哈希值跟数组的长度计算出应存入的位置
- int index = (数组长度 - 1) & 哈希值;
- 如果集合中存储的是自定义对象,必须要重写hashCode和equsl方法
3.判断当前位置是否为null,如果是null直接存入(添加元素)
4.如果位置不是null,表示有元素,则调用equals方法比较属性值
- 一样:不存 ------ 不一样:存入数组,形成链表
- JDK8以前:新元素存入数组,老元素挂在新元素下面
- JDK8以后:新元素挂在老元素下面
HashSet的三个问题
- 问题1.HashSet为什么存和取的顺序不一样?
- HashSet遍历是从数组的0索引开始,一条链表一条链表(或红黑树)的遍历,可能和存储时候的顺序不一样
- 问题2.HashSet为什么没有索引?
- HashSet不够纯粹,在底层是链表 数组 红黑树三种组合,定义谁都不合适。
- 假设使用数组的索引,但一个索引处可能会有链表,不可能链表上所有的数据都是一个索引
- 问题3.HashSet是利用什么机制保证数据去重复的
- HashCode方法 --> 哈希值 --> 确定元素添加在哪个位置
- equals方法 --> 比较内部的属性值是否相同(自定义对象要重写这两种方法)
练习:利用HashSet集合去除重复元素
需求:创建一个存储学生对象的集合,存储多个学生对象 使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象
public class A02_HashSetDemo2 { public static void main(String[] args) { //1。创建3个学生对象 Student s1 = new Student("朵朵", 5); Student s2 = new Student("小七", 7); Student s3 = new Student("钢镚", 9); Student s4 = new Student("朵朵", 5); //2。创建集合用来添加学生 HashSet<Student> hs = new HashSet<>(); //3。添加元素(重写hashCode和equal方法后) //true System.out.println(hs.add(s1)); //true System.out.println(hs.add(s2)); //true System.out.println(hs.add(s3)); //false System.out.println(hs.add(s4)); //4。打印集合 //[Student{name = 小七, age = 7}, Student{name = 钢镚, age = 9}, Student{name = 朵朵, age = 5}] System.out.println(hs); } }
LinkedHashSet
LinkedHashSet底层原理
- 有序、不重复、无索引
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序
public class A04_LinkedHashSetDemo { public static void main(String[] args) { //1。创建3个学生对象 Student s1 = new Student("朵朵", 5); Student s2 = new Student("小七", 7); Student s3 = new Student("钢镚", 9); Student s4 = new Student("朵朵", 5); //2。创建集合用来添加学生 LinkedHashSet<Student> lhs = new LinkedHashSet<>(); //3。添加元素(重写hashCode和equal方法后) //true System.out.println(lhs.add(s1)); //true System.out.println(lhs.add(s2)); //true System.out.println(lhs.add(s3)); //false System.out.println(lhs.add(s4)); //4。打印集合 //与添加顺序一样 //[Student{name = 朵朵, age = 5}, Student{name = 小七, age = 7}, Student{name = 钢镚, age = 9}] System.out.println(lhs); } }
数据去重
- 默认使用HashSet
- 如果要求去重且存取有序,才使用LinkedHashSet
到此这篇关于Java中的HashSet、LinkedHashSet集合解析的文章就介绍到这了,更多相关Java中的HashSet、LinkedHashSet内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!