当前位置: 首页 > news >正文

Java 集合:泛型、Set 集合及其实现类详解

参考资料:java入门到飞起

Java;泛型;Set 集合;TreeSet;HashSet;数据结构

一、引言

在 Java 编程中,集合框架是一个重要的组成部分,它提供了丰富的数据结构和算法来存储和操作数据。泛型与 Set 集合及其实现类在 Java 集合框架中占据着关键位置,对于提高代码的类型安全性、优化数据存储和检索具有重要意义。

二、泛型

2.1 泛型概述

2.1.1 泛型的介绍

泛型是 JDK 5 中引入的重要特性,它为 Java 语言增添了编译时类型安全检测机制。通过使用泛型,程序员可以在编译阶段就发现类型不匹配的错误,而不是在运行时才出现难以调试的错误,从而提高了程序的稳定性和可靠性。

2.1.2 泛型的好处
  1. 提前检测问题:将原本在运行时期可能出现的类型问题提前到编译期间,大大降低了运行时错误的发生概率,提高了程序的健壮性。
  2. 避免强制类型转换:使用泛型后,代码在获取和操作集合元素时无需进行显式的强制类型转换,使代码更加简洁、易读,同时也减少了因强制类型转换不当而引发的ClassCastException异常。
2.1.3 泛型的定义格式
  1. 单类型格式<类型>用于指定一种类型,尖括号内通常使用单个字母表示,常见的如<E>(表示元素类型,Element 的缩写)、<T>(表示一般类型,Type 的缩写)。例如,List<E>表示一个存储E类型元素的列表。
  2. 多类型格式<类型1,类型2…>用于指定多种类型,不同类型之间用逗号隔开,如<E,T><K,V>(常用于表示键值对中的键类型K和值类型V)。例如,Map<K, V>表示一个存储键值对的映射,其中键的类型为K,值的类型为V

三、Set 集合

3.1Set 集合概述和特点【应用】

Set 集合是 Java 集合框架中的一种重要类型,具有以下显著特点:

  1. 不允许重复元素:Set 集合中不能存储重复的元素,这使得 Set 集合在需要确保元素唯一性的场景中非常有用,例如去重操作。
  2. 无索引:Set 集合没有索引,这意味着无法像列表那样通过索引来访问元素,也不能使用普通的 for 循环进行遍历。

3.2Set 集合的使用【应用】

以下代码展示了如何使用 Set 集合存储字符串并进行遍历:

public class MySet1 {public static void main(String[] args) {//创建集合对象Set<String> set = new TreeSet<>();//添加元素set.add("ccc");set.add("aaa");set.add("aaa");set.add("bbb");//Set集合是没有索引的,所以不能使用通过索引获取元素的方法//遍历集合Iterator<String> it = set.iterator();while (it.hasNext()) {String s = it.next();System.out.println(s);}// 如果需要删除元素,可以使用 iterator.remove()// 注意:不能直接使用 set.remove(),否则会抛出 ConcurrentModificationException// 示例:删除 "aaa"iterator = set.iterator(); // 重新获取迭代器while (iterator.hasNext()) {String s = iterator.next();if (s.equals("aaa")) {iterator.remove(); // 安全删除}}System.out.println("-----------------------------------");for (String s : set) {System.out.println(s);}}
}

在上述代码中,首先创建了一个TreeSet对象(TreeSetSet接口的一个实现类),然后向集合中添加了一些字符串元素,其中包含重复的元素 “aaa”。通过Iterator迭代器和增强 for 循环两种方式对集合进行遍历,输出集合中的元素。可以看到,重复元素 “aaa” 只出现了一次,体现了 Set 集合不允许重复元素的特性。

Iterator迭代器介绍

Iterator(迭代器)是Java集合框架中的一个重要接口,用于遍历集合中的元素。它提供了一种统一的方式来访问集合元素,而不需要暴露集合的内部表示。核心方法如下:

  • boolean hasNext():判断是否还有下一个元素
  • E next():返回下一个元素
  • void remove():删除上次调用next()返回的元素(可选操作)

关键点​

  1. Set 的迭代器只能单向遍历(hasNext() + next()),不能反向遍历。
  2. 删除元素必须用 iterator.remove(),直接使用 set.remove() 会抛出 ConcurrentModificationException
  3. Set 是无序的​(除非使用 LinkedHashSet 或 TreeSet),所以遍历顺序可能与插入顺序不同。
  4. 增强 for 循环底层也是用 Iterator,但不能在遍历时删除元素(除非用 Iterator)。

四、TreeSet 集合

4.1 TreeSet 集合概述和特点【应用】

TreeSetSet接口的一个实现类,除了具备 Set 集合的基本特点(不允许重复元素、无索引)外,还具有以下特性:

  1. 元素排序TreeSet可以将存储的元素按照特定规则进行排序。
    • 自然排序:通过TreeSet()无参构造方法创建的集合,会根据元素的自然排序进行排序。所谓自然排序,是指元素所属的类实现Comparable接口,并在其中定义比较规则。
    • 比较器排序:通过TreeSet(Comparator comparator)带参构造方法创建的集合,会根据指定的比较器进行排序。比较器是一个实现了Comparator接口的对象,在其compare(T o1, T o2)方法中定义元素的比较规则。

4.2 TreeSet 集合基本使用【应用】

以下代码展示了如何使用TreeSet集合存储Integer类型的整数并进行遍历:

public class TreeSetDemo01 {public static void main(String[] args) {//创建集合对象TreeSet<Integer> ts = new TreeSet<Integer>();//添加元素ts.add(10);ts.add(40);ts.add(30);ts.add(50);ts.add(20);ts.add(30);//遍历集合for (Integer i : ts) {System.out.println(i);}}
}

在上述代码中,创建了一个TreeSet集合对象ts,并向其中添加了一些Integer类型的整数,包括重复的数字 30。通过增强 for 循环遍历集合时,输出的元素按照从小到大的顺序排列,体现了TreeSet集合的排序特性,并且重复元素只保留了一个。

4.3 自然排序 Comparable 的使用【应用】

4.3.1 案例需求

存储学生对象并遍历,使用TreeSet集合的无参构造方法,要求按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序。

4.3.2 实现步骤
  1. 创建 TreeSet 集合:使用空参构造创建TreeSet集合,因为无参构造方法使用自然排序对元素进行排序。
  2. 实现 Comparable 接口:自定义的Student类需要实现Comparable接口,通过重写compareTo(T o)方法来定义元素的比较规则。
  3. 重写 compareTo 方法:在重写方法时,要按照指定的主要条件(年龄从小到大)和次要条件(年龄相同时按姓名字母顺序)编写比较逻辑。
4.3.3 代码实现

学生类

快速构建构造方法:快捷键:Alt+Insert

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) {//按照对象的年龄进行排序//主要判断条件: 按照年龄从小到大排序int result = this.age - o.age;//次要判断条件: 年龄相同时,按照姓名的字母顺序排序result = result == 0? this.name.compareTo(o.getName()) : result;return result;}
}
PS:Comparable是干嘛的?

Comparable 是 Java 提供的一个 ​泛型接口,定义在 java.lang 包中,它只有一个方法:

public interface Comparable<T> {int compareTo(T o);  // 比较当前对象与参数对象 o 的大小
}

​为什么 Student 类要实现 Comparable<Student>?​​

如果 Student 类实现了 Comparable<Student>,那么:

  1. ​可以调用 Collections.sort() 或 Arrays.sort() 对 Student 对象列表进行排序​:
    List<Student> students = new ArrayList<>();
    // 添加学生对象...
    Collections.sort(students);  // 自动调用 compareTo 方法排序
  2. ​可以在 TreeSet 或 TreeMap 中自动排序​(因为它们依赖 Comparable 或 Comparator 进行排序)。
  3. ​可以方便地进行对象比较,例如在 if (student1.compareTo(student2) > 0) 这样的逻辑中使用。

​如何实现 Comparable<Student>?​​

你需要在 Student 类中 ​重写 compareTo() 方法,定义如何比较两个 Student 对象。例如:

public class Student implements Comparable<Student> {private String name;private int age;private double score;// 构造方法、getter/setter 省略...@Overridepublic int compareTo(Student other) {// 按分数升序排序return Double.compare(this.score, other.score);// 或者按姓名升序排序(字符串比较)// return this.name.compareTo(other.name);// 或者按年龄降序排序(注意负号)// return Integer.compare(other.age, this.age);}
}

​示例说明​:

  • Double.compare(this.score, other.score):比较两个学生的分数(升序)。
  • this.name.compareTo(other.name):按姓名字典序排序(升序)。
  • Integer.compare(other.age, this.age):按年龄降序排序(因为 other.age - this.age 是降序逻辑)。

测试类

public class MyTreeSet2 {public static void main(String[] args) {//创建集合对象TreeSet<Student> ts = new TreeSet<>();//创建学生对象Student s1 = new Student("zhangsan", 28);Student s2 = new Student("lisi", 27);Student s3 = new Student("wangwu", 29);Student s4 = new Student("zhaoliu", 28);Student s5 = new Student("qianqi", 30);//把学生添加到集合ts.add(s1);ts.add(s2);ts.add(s3);ts.add(s4);ts.add(s5);//遍历集合for (Student student : ts) {System.out.println(student);}}
}

在上述代码中,Student类实现了Comparable接口,并在compareTo方法中定义了排序规则。在测试类中,创建了TreeSet集合并添加了多个学生对象,遍历集合时,学生对象按照年龄从小到大排序,年龄相同的按照姓名字母顺序排序。

4.4 比较器排序 Comparator 的使用【应用】

4.4.1 案例需求

存储老师对象并遍历,创建TreeSet集合使用带参构造方法,要求按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序。

4.4.2 实现步骤
  1. 使用带参构造创建 TreeSet 集合TreeSet集合的带参构造方法使用比较器排序对元素进行排序。
  2. 实现 Comparator 接口:让集合构造方法接收一个实现了Comparator接口的对象,在其compare(T o1, T o2)方法中定义元素的比较规则。
  3. 重写 compare 方法:按照指定的主要条件和次要条件编写比较逻辑。
4.4.3 代码实现

老师类

public class Teacher {private String name;private int age;public Teacher() {}public Teacher(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 "Teacher{" +"name='" + name + '\'' +", age=" + age +'}';}
}

测试类

public class MyTreeSet4 {public static void main(String[] args) {//创建集合对象TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {@Overridepublic int compare(Teacher o1, Teacher o2) {//o1表示现在要存入的那个元素//o2表示已经存入到集合中的元素//主要条件int result = o1.getAge() - o2.getAge();//次要条件result = result == 0? o1.getName().compareTo(o2.getName()) : result;return result;}});//创建老师对象Teacher t1 = new Teacher("zhangsan", 23);Teacher t2 = new Teacher("lisi", 22);Teacher t3 = new Teacher("wangwu", 24);Teacher t4 = new Teacher("zhaoliu", 24);//把老师添加到集合ts.add(t1);ts.add(t2);ts.add(t3);ts.add(t4);//遍历集合for (Teacher teacher : ts) {System.out.println(teacher);}}
}

在上述代码中,测试类通过TreeSet的带参构造方法传入一个匿名内部类,该内部类实现了Comparator接口,并在compare方法中定义了老师对象的比较规则。在创建老师对象并添加到集合后,遍历集合时老师对象按照年龄和姓名的指定规则进行排序。

4.5 两种比较方式总结

  1. 自然排序:自定义类实现Comparable接口,重写compareTo方法,集合根据该方法的返回值进行排序。这种方式适用于类本身具有自然的比较顺序,并且在多个地方都需要使用相同排序规则的情况。
  2. 比较器排序:创建TreeSet对象时传递一个实现了Comparator接口的对象,重写compare方法,集合依据此方法的返回值进行排序。当自然排序不能满足特定需求,或者需要针对不同场景使用不同排序规则时,比较器排序更为灵活。
  3. 使用选择:在实际使用中,通常优先考虑自然排序。当自然排序无法满足需求时,必须使用比较器排序来实现自定义的排序逻辑。

相关文章:

  • 信息系统项目管理工程师备考计算类真题讲解六
  • 用交换机连接两台电脑,电脑A读取/写电脑B的数据
  • 榜单持久化
  • python实战项目63:获取腾讯招聘信息内容并进行统计分析
  • Windows 各版本查找计算机 IP 地址指南
  • Elasticsearch(ES)中的脚本(Script)
  • 【人工智能】Ollama 负载均衡革命:多用户大模型服务的高效调度与优化
  • WT2000T专业录音芯片:破解普通录音设备信息留存、合规安全与远程协作三大难题
  • web刷题笔记
  • 代码分享:python实现svg图片转换为png和gif
  • 【计算机视觉】CV项目实战- SORT 多目标跟踪算法
  • 【模板匹配】图像处理(OpenCV)-part10
  • 学习海康VisionMaster之卡尺工具
  • 操作系统学习笔记
  • Representation Flow for Action Recognition论文笔记
  • 破茧成蝶:阿里云应用服务器让传统 J2EE 应用无缝升级 AI 原生时代
  • CSS清楚默认样式
  • CSS外边距合并现象
  • [架构之美]Ubuntu源码部署APISIX全流程详解(含避坑指南)
  • C++学习:六个月从基础到就业——C++学习之旅:STL迭代器系统
  • 宁夏回族自治区人大环境与资源保护委员会主任委员张柏森被查
  • 对话地铁读书人|企业公关吴丑丑:阅读中相遇又重逢
  • 湘江半程马拉松赛女配速员“跑崩”,晕倒在终点?组委会回应
  • 人民日报聚焦外贸“重镇”福建晋江:多元化布局扩大“朋友圈”
  • 平均25岁,天津茱莉亚管弦乐团进京上演青春版《春之祭》
  • 泽连斯基:俄军违反停火承诺,20日10时起前线俄炮击增加