HashSet、LinkedHashSet、TreeSet

2024-01-05 10:28

本文主要是介绍HashSet、LinkedHashSet、TreeSet,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Set系列集合

  • 无序:存取顺序不一致
  • 不重复:可以去除重复
  • 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引获取元素

Set集合的实现类

  • HashSet:无序、无索引、不重复
  • LinkedHashSet:有序、不重复、无索引
  • TreeSet:可排序、不重复、无索引

Set系列集合没有特有的方法,直接继承顶层接口Collection中的所有方法并使用。

1.HashSet

HashSet底层:

  • HashSet集合底层采用哈希表存储数据
  • 哈希表是一种对于增删改查数据性能都比较好的结构

哈希表的组成:

  • JDK8之前:数组+链表
  • JDK8开始:数组+链表+红黑树

HashSet底层存储数据:

通过调用hashCode方法计算对象的哈希值,然后根据哈希值和数组长度计算出对象应存入哈希表的位置,然后该对象再调用equals方法和当前位置的所有对象依次比较,若存在相同属性值对象,则舍弃该对象不存,若不存在属性值相同对象,则将该对象存入当前位置。

注意:一定要重写hashCode和equals方法,这样才能保证实现不重复

哈希值细节:

  1. 根据hashCode方法算出来的int类型的整数
  2. 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
  3. 一般情况下,都会重写hashCode方法,利用对象内部的属性值计算哈希值
  4. 如果没有重写hashCode方法,不同对象计算出来的哈希值是不同的
  5. 如果重写了hashCode方法,不同对象只有属性值相同,计算出来的哈希值就是一样的
  6. 小部分情况下,不同属性值或者不同地址值计算出来的哈希值也有可能一样。(哈希碰撞)

HashSet底层原理:

  1. 创建一个默认长度16,默认加载因子0.75的数组,数组名table
  2. 根据元素的哈希值跟数组的长度计算出应存入的位置
  3. 判断当前位置是否为null,如果是null直接存入
  4. 如果位置不为null,表示当前位置已经有元素,则调用equals方法比较属性值
  5. 一样:表明重复了,不存 ;不一样:存入数组,形成链表(JDK8以前:新元素存入数组,老元素挂在新元素下面;JKD8开始:新元素直接挂在老元素下面)
  6. 当元素个数达到16*0.75=12个时,数组会扩容为原来的2倍长度变成32

JDK8以后,当链表长度超过8且数组长度大于等于64时,自动转换为红黑树

如果集合中存储的是自定义对象,必须重写hashCodeequals方法

三个问题:

HashSet为什么存和取的顺序不一样?

HashSet为什么没有索引?

HashSet是利用什么机制保证数据去重的?

2.LinkedHashSet

  • 有序、不重复、无索引
  • 有序指的是存储和取出元素的顺序一致
  • 原理:底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序

总结:

1.LinkedHashSet集合的特点和原理是怎样的?

  • 有序、不重复、无索引
  • 底层基于哈希表,使用双链表记录添加顺序

2.以后如果要数据去重,我们应该使用哪个?

  • 默认使用HashSet,因为HashSet的效率更高
  • 如果要求去重且存取有序,才使用LinkedHashSet

3.TreeSet

TreeSet的特点:

  • 不重复、无索引、可排序
  • 可排序:按照元素的默认规则(由小到大)排序
  • TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都比较好

TreeSet集合默认规则

  • 对于数值类型:Integer,Double,默认是按照从小到大的顺序进行排序
  • 对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序
public class TreeSetDemo1 {public static void main(String[] args) {//创建TreeSet集合对象TreeSet<Integer> ts = new TreeSet<>();//添加元素ts.add(5);ts.add(3);ts.add(1);ts.add(4);ts.add(2);//打印集合System.out.println(ts);//[1, 2, 3, 4, 5]//三种通用遍历方式遍历//迭代器遍历/*Iterator<Integer> it = ts.iterator();while (it.hasNext()){int i = it.next();System.out.println(i);}*///增强for遍历/*for (Integer t : ts) {System.out.println(t);}*///lambda表达式遍历ts.forEach(i-> System.out.println(i));}
}

TreeSet两种比较方式

方式一:

默认排序/自然排序:Javabean类实现Comparable接口指定比较规则

举例:学生有姓名、年龄属性,现要求按照学生的年龄进行排序,同年龄按照姓名字母排序

public class Student2 implements Comparable<Student2>{//通过compareTo方法指定比较规则//this:表示当前要添加的元素//o:表示已经在红黑树中存在的元素//返回值://正数:表示当前要添加的元素是大的,存右边//负数:表示当前要添加的元素是小的,存左边//0:表示当前要添加的元素已经存在,舍弃@Overridepublic int compareTo(Student2 o) {//指定排序规则//按照年龄升序进行排列return this.getAge() - o.getAge();}
}
public class TreeSetDemo2 {public static void main(String[] args) {Student2 s1 = new Student2("zhangsan",23);Student2 s2 = new Student2("lisi",24);Student2 s3 = new Student2("wangwu",25);TreeSet<Student2> ts = new TreeSet<>();ts.add(s1);ts.add(s2);ts.add(s3);System.out.println(ts);}
}

方式二:

比较器排序:创建TreeSet对象时,传递比较器Comparator指定规则

举例:存入字符串"b","ac","bc","abc",按照长度排序,如果一样长则按照首字母排序

public class TreeSetDemo3 {public static void main(String[] args) {/*TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {int i = o1.length() - o2.length();i = i == 0 ? o1.compareTo(o2) : i;return i;}});*///o1:表示当前要添加的元素//o2:表示在红黑树中已经存在的元素TreeSet<String> ts = new TreeSet<>((o1,o2)->{//按照长度排序int i = o1.length() - o2.length();//如果长度一样按照首字母排序i = i == 0 ? o1.compareTo(o2) : i;return i;});ts.add("c");ts.add("ac");ts.add("bc");ts.add("abc");System.out.println(ts);}
}

使用原则:默认使用第一种,如果第一种不能满足当前需求,就使用第二种

总结:

1.如果想要集合中的元素可重复

  • 用ArrayList集合,基于数组的。(用的最多)

2.如果想要集合中的元素可重复,而且当前的增删操作明显多于查询

  • 用LinkedList集合,基于链表的

3.如果想对集合中的元素去重

  • 用HashSet集合,基于哈希表的。(用的最多)

4.如果想对集合中的元素去重,而且保证存取有序

  • 用LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet

5.如果想对集合中的元素进行排序

  • 用TreeSet集合,基于红黑树。后续也可以用List集合实现排序

这篇关于HashSet、LinkedHashSet、TreeSet的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/572553

相关文章

Java重修笔记 第四十八天 TreeSet 类、TreeMap 类

TreeSet 类 1. TreeSet 底层是 TreeMap 2. 使用默认构造器创建的 TreeSet 对象,添加顺序和取出顺序不是有序的 3. 如果添加的是字符串或数字,它们默认会按照字母顺序或数值顺序进行排序 4. 可以在构造器中传入一个 Comparator 比较器来手动制定比较规则,之后传入的数据会根据改规则自动进行比较排序,如果根据比较器比较出的结果是相同的,即 com

容器第七课,Set,HashSet基本用法,源码分析

Set接口 Set接口是Collection接口的子接口,Set接口没有提供额外的方法,Set接口的特性是容器类中的元素是i没有顺序的,而且不可以重复。Set容易可以在数学中“集合”的概念相对应。J2SDK API中 所提供的Set容器类有HashSet、TreeSet等 package com.pkushutong.Collection;import java.util.HashSet;

Set判断重复,TreeSet排序

Set集合中,无序,不能重复,如何判断对象重复? 在对象的bean中实现hashCode 和 equals方法,选定属性,根据选定的属性判断两个对象是否相等。 TreeSet类 TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是

Java集合框架(Set与Map,HashSet与HashMap,TreeSet与TreeMap)

这是一个介绍集合类,数组以及容器关系的截图,便于我们对集合的理解。 一.Set和Map Set代表一种集合元素无序、不可重复的集合,Map则代表一种由多个key-value(键-值)对组成的集合。 从表面上看,它们之间的相似性很少,但实际上Map和Set之间有莫大的联系。可以说,Map集合是Set集合的扩展。 如果只考察Map集合的Key,不难发现,这些Map集合的key具有一个特

第一章 集合框架和泛型(ArrayList/LinkedList/HashSet/HashMap/泛型集合/Collections算法类)

第一章 集合框架和泛型 一、Collection 1、Collection 接口存储一组不唯一,无序的对象 二、List List 接口存储一组不唯一,有序(插入顺序)的对象 1.ArrayList 实现了长度可变的数组,在内存中分配连续的空间优点:遍历元素和随机访问元素的效率比较高ArrayList类是List接口的一个具体实现类ArrayList对象实现了可变大小

Java重修笔记 第四十五天 LinkedHashSet 类

LinkedHashSet 类 1. LinkedHashSet 是 HashSet 的子类,继承 HashSet 的方法 2. LinkedHashSet 的底层是 LinkedHashMap ,底层维护了一个数组加双向链表的组合 3. LinkedHashSet 根据元素的 hashCode 值来决定元素在 table 数组上的存储位置,同时使用链表结构来维护元素的次序(after

12. TreeSet的内部实现原理是什么?它是如何实现排序的?

TreeSet 是 Java 集合框架中的一个实现类,它实现了 NavigableSet 接口,并且是基于 TreeMap 实现的。TreeSet 的主要特性是元素自动排序,即元素会按照自然顺序或自定义的比较器顺序进行排序。在底层,TreeSet 是通过红黑树(Red-Black Tree)来实现的,这是一种自平衡的二叉搜索树。 TreeSet 的内部实现原理 1. 基于 TreeMa

Java重修笔记 第四十三天 Set 集合、HashSet 类

Set 接口 1. 它是无序的(添加和取出的顺序不一致,但取出的结果是固定的),没有索引 2. Set 接口也是 Collection 的子接口,所以继承了 Collection 的方法 3. Set 接口的遍历方式有两种,迭代器和增强 for 循环,但是不能使用索引遍历 HashSet 类 1. 底层是一个 HashMap,可以把 HashSet 看成 HashMap 2

Java重修笔记 第四十四天 HashSet 添加元素规则、树化规则和扩容规则

添加元素规则 1. HashSet 底层是 HashMap,所以他俩的逻辑是一样的 2. 添加一个元素时,先得到 hash 值再转成索引值(Hash值来自于却不等于HashCode()的值) 3. 看这个存储数据表 table 的索引位置是否已经存放有元素 4. 如果没有,直接加入  5. 如果有,则调用对象的 equals() 方法逐一进行比较,如果有相同的,就放弃添加,如果都不相

10. Java 中的 HashSet 和 HashMap 有什么区别?

HashSet 和 HashMap 是 Java 集合框架中的两个重要类,它们都基于哈希表(Hash Table)实现,并且在许多方面共享类似的特性。然而,它们的用途和实现上有一些重要的区别。 1. 功能和用途 HashSet: HashSet 是一个实现了 Set 接口的集合类,用于存储唯一的元素。集合中的元素不能重复。 它不保证集合的顺序(插入顺序也不保证),并且不允许存储 nu