详解Set集合中实现类:LinkedHashSet以及TreeSet

  • 1.LinkedHashSet概述
  • 2.TreeSet概述
    • 2.1 自然排序
      • 2.1.1 TreeSet存储常见引用数据类型
      • 2.1.2 TreeSet存储自定义类的对象进行自然排序
    • 2.2 比较器排序
      • 2.2.1 使用比较器进行对自定义类对象进行排序
      • 2.2.2 使用比较器可以对ArrayList集合和数组进行排序
  • 3.练习:产生10个0-19之间的随机数要求随机数不能重复
  • 总结

Set集合具有两个特点:无序性与不允许元素重复。前面我们也学习HashSet(作为Set接口的主要实现类),底层结构是哈希表。

JDK1.7:HashSet的底层数据结构为数组加链表,JDK1.8:HashSet的底层数据结构为数组加链表加红黑树。

HashSet集合的特点为元素无序,且元素是唯一的。要保证元素的唯一性,是靠元素重写hashCode()方法与equals()方法来进行保证的,如果元素不重写这两个方法,则无法保证元素的唯一性。
我们要合理的重写hashCode()方法,是为了减少调用equals()方法的次数,即减少元素碰撞的次数。

而且需要注意的是:使用HashSet方法存储数据时,在遍历时不能用普通for循环,因为Set接口中没有get()方法。可以使用增强for循环或者使用迭代器进行遍历。

public class MyTest {public static void main(String[] args) {HashSet<String> hashSet = new HashSet<>();hashSet.add("aaa");hashSet.add("aaa");hashSet.add("bbb");hashSet.add("bbb");hashSet.add("ccc");hashSet.add("ddd");//使用迭代器进行遍历Iterator<String> iterator = hashSet.iterator();while (iterator.hasNext()) {String s = iterator.next();System.out.println(s);}System.out.println("==============");//使用增强for循环进行遍历for (String s : hashSet) {System.out.println(s);}}
}

了解了HashSet之后,本节学习Set接口中另外两个实现类:LinkedHashSet以及TreeSet。


1.LinkedHashSet概述

HashSet是Set接口的典型表现,大多数情况下使用Set集合时都使用这个实现类。

HashSet和LinkedHashSet按照Hash算法来存储集合中的元素,因此具有很好的存取和查找的功能。

HashSet和LinkedHashSet集合判断两个元素是否相等的标准:两个对象hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等视为重复的元素,不能进行添加。因此,存储到HashSet和LinkedHashSet的元素要重写hashCode()方法以及equals()方法。

LinkedHashSet是HashSet的子类,它在HashSet的基础上,在结点中增加两个属性before和after维护节点的前后添加顺序。

LinkedHashSet的底层数据结构链表加哈希表,元素有序且唯一。链表保证了元素有序(这里的有序,因为在节点处增加了before和after属性维护节点的前后添加顺序),哈希表保证元素唯一。

import java.util.LinkedHashSet;
public class MyTest2 {public static void main(String[] args) {//LinkedHashSet的底层数据结构链表加哈希表,元素有序且唯一。// 链表保证了元素有序(这里的有序,因为在节点处增加了before和after属性维护节点的前后添加顺序),哈希表保证元素唯一。LinkedHashSet<Integer> list = new LinkedHashSet<>();list.add(100);list.add(200);list.add(300);list.add(400);list.add(500);list.add(100);list.add(200);list.add(300);list.add(400);list.add(500);//使用增强for循环进行遍历for (Integer integer : list) {System.out.println(integer);}}
}

运行后的结果为:


2.TreeSet概述

TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素的处于排序状态,同时元素也是唯一的。

使用元素的自然排序(Compareable)对元素进行排序,或者根据创建set时提供的Comparator进行排序,具体取决于使用的构造方法。


2.1 自然排序

public TreeSet()构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。插入该 set 的所有元素都必须实现 Comparable 接口。另外,所有这些元素都必须是可互相比较的。


2.1.1 TreeSet存储常见引用数据类型

TreeSet存储Integer类型的元素并遍历
存储下列元素: 20 , 18 , 23 , 22 , 17 , 24, 19 , 18 , 24

public class MyTest {public static void main(String[] args) {///TreeSet:底层数据结是二叉树的结构,他能保证元素的唯一性,而且还能对元素进行排序。TreeSet<Integer> treeset = new TreeSet<>();treeset.add(20);treeset.add(18);treeset.add(23);treeset.add(22);treeset.add(17);treeset.add(24);treeset.add(19);treeset.add(18);treeset.add(24);System.out.println("使用TreeSet排序后的集合为:");for (Integer integer : treeset) {System.out.println(integer);}}
}

运行后的结果为:


注意:使用TreeSet集合进行元素的自然排序,那么对元素有要求,要求这个元素 必须实现Comparable接口 否则无法进行自然排序

我们进入了Integer类,发现了实现了Compareable接口,重写了CompareTo方法,进而可以进行自然排序。


TreeSet保证元素唯一和自然排序的原理与图解:


2.1.2 TreeSet存储自定义类的对象进行自然排序

案例:定义一个自定义的Student类,成员属性为姓名和年龄,使用TreeSet排序方式中的自然排序方式,按照年龄大小对集合中的元素进行排序。

/*自然排序:采用空参构造,用的就是自然排序,自然排序对元素有要求,
要求元素实现 Comparable 接口,重写 compareTo这个方法*/
public class Student implements Comparable<Student> {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Student o) {//用学生的年龄进行大小进行排序,比较的逻辑由我们来写//return 0;//根据此方法返回值的正负0 来决定元素在二叉树中所存放的位置。//如果年龄不相等,使用年龄的相减的返回值int age = this.age - o.age;//如果年龄相等的话,并不能说明是同一个对象,还需要比较姓名int num=age==0? this.name.compareTo(o.name):age;return num;}
}import java.util.TreeSet;public class MyTest {public static void main(String[] args) {TreeSet<Student> treeset = new TreeSet<>();treeset.add(new Student("张三",25));treeset.add(new Student("张三丰",25));treeset.add(new Student("李四",22));treeset.add(new Student("王五",21));treeset.add(new Student("赵六",23));treeset.add(new Student("钱七",24));treeset.add(new Student("陈八",20));for (Student student : treeset) {System.out.println("年龄:"+student.getAge()+"==="+"姓名:"+student.getName());}}
}

运行后的结果为:


练习:定义一个自定义的Student类,成员属性为姓名和年龄,使用TreeSet排序方式中的自然排序方式,按照姓名长度的大小对集合中的元素进行排序。

public class Student implements Comparable<Student> {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Student stu) {//return 0;//按照姓名的长度来排序int num = this.name.length() - stu.name.length();//姓名长度一样了,还的比较姓名内容一样不int num2 = num == 0 ? this.name.compareTo(stu.name) : num;//姓名长度一样了,内容一样了,还得比较年龄是否一样int num3 = num2 == 0 ? this.age - stu.age : num2;return num3;}
}import java.util.TreeSet;public class MyTest {public static void main(String[] args) {TreeSet<Student> treeset = new TreeSet<>();treeset.add(new Student("Jack",24));treeset.add(new Student("Lebron",23));treeset.add(new Student("Lebron",23));treeset.add(new Student("Abubli",23));treeset.add(new Student("Janey",25));treeset.add(new Student("Tom",26));treeset.add(new Student("Abubli",26));for (Student student : treeset) {System.out.println("姓名"+student.getName()+"====="+"年龄"+student.getAge());}}
}

运行后的结果为:


2.2 比较器排序

public TreeSet(Comparator<? super E> comparator)构造一个新的空 TreeSet,它根据指定比较器进行排序。插入到该 set 的所有元素都必须能够由指定比较器进行相互比较。


2.2.1 使用比较器进行对自定义类对象进行排序

定义一个自定义的Student类,成员属性为姓名和年龄,使用TreeSet排序方式中的比较器排序方式,按照年龄大小对集合中的元素进行排序。

//自定义类Student类
public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}//定义实现比较器接口的子类
import java.util.Comparator;
//使用比较器排序,需要实现Comparator接口,并且重写compare方法
public class MyComparator implements Comparator<Student> {@Overridepublic int compare(Student s1, Student s2) {//根据年龄大小来进行排序int num = s1.getAge() - s2.getAge();//如果年龄相等不代表是同一个对象,还需要比较姓名的内容是否相同int num2=num==0?s1.getName().compareTo(s2.getName()):num;return num2;}
}import java.util.TreeSet;
public class MyTest {public static void main(String[] args) {/*  TreeSet(Comparator < ? super E > comparator)构造一个新的空 TreeSet,它根据指定比较器进行排序。*///如果使用有参构造,那就是比较器排序MyComparator myComparator = new MyComparator();TreeSet<Student> treeSet = new TreeSet<>(myComparator);treeSet.add(new Student("Jack",24));treeSet.add(new Student("Lebron",23));treeSet.add(new Student("Lebron",23));treeSet.add(new Student("Abubli",23));treeSet.add(new Student("Janey",25));treeSet.add(new Student("Tom",26));treeSet.add(new Student("Abubli",26));for (Student student : treeSet) {System.out.println("年龄:"+student.getAge()+"姓名:"+student.getName());}}
}

运行后的结果为:

总而言之:比较器的方式,采用有参构造,传入比较器,重写compare()方法根据此方法,返回值的正负0,来决定元素放置的左右顺序。


2.2.2 使用比较器可以对ArrayList集合和数组进行排序

1.使用Comparator比较器可以用来比较ArrayList集合中元素大小,进行排序

注意:将集合中的元素进行排序的sort方法中需要传入实现Comparator接口的子类对象

import java.util.ArrayList;
import java.util.Comparator;public class MyTest2 {public static void main(String[] args) {ArrayList<Integer> list= new ArrayList<>();list.add(300);list.add(500);list.add(200);list.add(300);list.add(100);list.add(400);//对集合进行从大到小的排序//将集合中的元素进行排序的sort方法中需要传入实现Comparator接口的子类对象list.sort(new Comparator<Integer>() {@Overridepublic int compare(Integer a, Integer b) {return b-a;}});System.out.println(list);}
}

运行后的结果为:


2.使用Comparator比较器可以用来比较引用数据类型数组中元素大小,进行排序

数组工具类中的sort方法可以传入实现比较器的子类对象,并重写compare方法

import java.util.Arrays;
import java.util.Comparator;public class MyTest3 {public static void main(String[] args) {Integer[] arr = {2, 3, 6, 5, 4, 1};//数组工具类中的sort方法可以传入实现比较器的子类对象,并重写compare方法Arrays.sort(arr,new Comparator<Integer>(){@Overridepublic int compare(Integer a, Integer b) {return b-a;}});System.out.println(Arrays.toString(arr));}
}

运行后的结果为:

3.练习:产生10个0-19之间的随机数要求随机数不能重复

编写一个程序,获取10个1至20的随机数,要求随机数不能重复。 并把最终的随机数输出到控制台。
选HashSet 可以不重复
选TreeSet 不重复还可以排序

分析:
(1)定义一个HashSet集合或者TreeSet集合
(2)产生随机数,把随机数添加到集合中
(3)判断集合的长度,使用while循环进行实现

import java.util.HashSet;
import java.util.Random;
import java.util.TreeSet;public class MyTest {public static void main(String[] args) {/* 编写一个程序,获取10个0至19的随机数,要求随机数不能重复。并把最终的随机数输出到控制台。*/Random random = new Random();TreeSet<Integer> set = new TreeSet<>();// HashSet<Integer> set = new HashSet<>();while(set.size()<10){int num = random.nextInt(20);set.add(num);}System.out.println(set);}
}

总结

本节主要介绍了Set集合的两个实现类LinkedHashSet以及TreeSet集合,其中LinkedHashSet是HashSet集合的子类。LinkedHashSet在HashSet的基础上,在结点中增加两个属性before和after维护节点的前后添加顺序。

而TreeSet需要我们掌握的是两种排序方法:使用空参构造的自然排序法以及使用比较器进行比较器排序。

JavaSE学习笔记 详解Set集合中实现类:LinkedHashSet以及TreeSet相关推荐

  1. 堆排序怎么建立初始堆_学习笔记-详解堆排序

    本文目的 上一章节已经详细的向大家介绍过排序的相关概念(详见学习笔记-排序简单介绍) ,本文旨在为大家详细的介绍堆排序. 堆排序 堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序 ...

  2. c++ 冒泡排序_学习笔记-详解冒泡排序

    本文目的 上一章节已经详细的向大家介绍过排序的相关概念学习笔记-排序简单介绍,本文旨在为大家详细的介绍冒泡排序. 冒泡排序 冒泡排序(Bubble Sort),是一种较简单的排序算法,是一种交换类排序 ...

  3. 小白180天学习笔记详解:零基础如何学习人工智能

    人工智能是大家公认的热点和未来趋势,目前对人工智能感兴趣的学生不在少数.一方面人工智能现在已经上升到国家发展战略层面,高校和专业的人工智能培训机构为应对人才缺口,开始着手培养人工智能人才:另一方面,人 ...

  4. 学习笔记——详解马尔可夫,马尔可夫链,马尔可夫模型,隐马

    目录 (一),马尔可夫 (Markov)综述 1.1,随机过程是啥玩意儿 1.2. 马尔可夫链 (Markov Chain)又是什么鬼 1.3一个经典的马尔科夫链实例 (二), 隐马尔可夫 HMM 2 ...

  5. C#学习笔记--详解委托,事件与回调函数

    .Net编程中最经常用的元素,事件必然是其中之一.无论在ASP.NET还是WINFrom开发中,窗体加载(Load),绘制(Paint),初始化(Init)等等. "protected vo ...

  6. 终于学会数组的使用啦~~~------C语言数组学习笔记详解

    本篇文章是对C语言数组内容的学习,将为初学数组的小伙伴们详细剖析数组,相信大家一定可以有所收获,接下来让我们一起学习吧!

  7. 数学建模学习笔记——优劣解距离法(评价类)

             

  8. 嵌入式开发学习笔记5-了解单片机中的特殊功能寄存器(寄存器B、累加器A和程序状态字PSW)

    嵌入式开发学习笔记5-了解单片机中的特殊功能寄存器(寄存器.累加器和程序状态字) 累加器A 寄存器B 程序状态字PSW 累加器A 累加器A是ACC(Accumulator)的缩写,累加器A是一个具有特 ...

  9. 嵌入式开发学习笔记6-了解单片机中的特殊功能寄存器(定时器/计数器)

    嵌入式开发学习笔记6-了解单片机中的特殊功能寄存器(定时器/计数器) 定时器/计数器简介 TH与TL TMOD工作模式寄存器 TCON定时器控制寄存器 定时器操作实例 定时器/计数器简介 在单片机中, ...

最新文章

  1. python使用matplotlib可视化饼图(pie plot)、可视化嵌套的环形饼图(Nested circular pie chart)
  2. 二、JavaScript语言--JS基础--JavaScript进阶篇--DOM对象 控制HTML元素
  3. vue项目实现记住密码到cookie功能
  4. boost::mpl::minus相关的测试程序
  5. 得到选择框句柄 怎么操作_电脑版微信怎么多开?最简单的三种电脑版微信多开教程...
  6. “烟花”来势汹汹!用数据可视化告诉你:台风最爱在哪登陆?
  7. (计算机组成原理)第七章输入和输出系统-第二节2:外部设备之外存储器(磁盘的基本结构,磁盘阵列)
  8. php7.2连接mysql8_兼容 php 7.2 及 mysql 8
  9. 根据我4年Facebook营销经验,和大家聊聊做不好Facebook广告的原因!
  10. linux 拷机软件,拷机软件跑起来,OS X平台下OpenGL初体验
  11. 数据库、数据库系统、数据库管理系统
  12. linux下直接复制文件内容到剪切板
  13. sessionStorage跨标签取值
  14. [转]一淘网是如何实现系统架构的
  15. 论文编辑——插入公式编号并对齐、插入图表编号、正文引用各类编号
  16. Java 算法之三色旗
  17. 常用矩阵向量求导公式
  18. 解决:Excel打开文件,内容不显示
  19. echarts模拟迁徙图
  20. matlab实现注册账号,创建账户

热门文章

  1. 终于进了腾讯,记录一下我作为一名测试员磕磕碰碰的三个月找工作经历...
  2. windows系统如何使用命令检测网络
  3. Protocol Buffer 时间类型定义
  4. dnf剑魂buff等级上限_DNF剑魂职业定位、装备、加点、猴戏、新老换装详解
  5. 【CSS】静态螺纹进度条和静态流程布局
  6. 重构于 Vite:我如何做 SSG、静态资源发布以及自动化部署
  7. java中什么是空指针异常_JAVA中的空指针异常如何处理?
  8. word2007表格计算机,电脑员好做吗?使用word2007表格?
  9. 【5G系列】RLC (Radio Link Control )协议详解
  10. java支付宝rsa2签名,使用支付宝SDK进行RSAj加签验签以及加密解密