Java类集框架-Set
Set集合
Set集合概述和特点
-
不可以存储重复元素
-
存取的顺序不一致
-
没有索引,不能使用普通for循环遍历
代码实现
案例:存储字符串并遍历
public class Demo{
public static void main(String[] args) {
//创建集合对象
Set<String> set = new TreeSet<>();
//添加元素
set.add("ccc");
set.add("aaa");
set.add("aaa");
set.add("bbb");
//(1)遍历集合
Iterator<String> it = set.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("-----------------------------------");
//(2)遍历集合
for (String s : set) {
System.out.println(s);
}
}
}
遇到重复元素, set集合会自动帮我们去重!
Set集合的实现——TreeSet
特点
-
不可以存储重复元素
-
没有索引
-
可以将元素按照规则进行排序
-
TreeSet():根据其元素的自然排序进行排序
-
TreeSet(Comparator comparator) :根据指定的比较器进行排序
-
(1)自然排序-Comparable
按照字典序,进行排序。
public class Demo03 {
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<>();
ts.add("aaa");
ts.add("bb");
ts.add("cc");
ts.add("dd");
ts.add("a");
System.out.println(ts);
//[a, aaa, bb, cc, dd]
}
}
案例(一):
存储学生对象并遍历,创建TreeSet集合使用无参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
实现步骤:
- 使用空参构造创建TreeSet集合。用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
- 自定义的Student类实现Comparable接口。自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
- 重写接口中的compareTo方法。重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
代码实现:
public class Demo03 {
public static void main(String[] args) {
TreeSet<Student> stu = new TreeSet<>();
Student s1 = new Student("小花花",27);
Student s2 = new Student("小花",29);
Student s3 = new Student("小明",25);
Student s4 = new Student("小黑",25);
stu.add(s1);
stu.add(s2);
stu.add(s3);
stu.add(s4);
System.out.println(stu);
//[Student{name='小明', age=25}, Student{name='小花花', age=27}, Student{name='小花', age=29}]
}
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
int result = this.age - o.age;
return result;
}
}
上述代码中,需要深究的是我们重写的comparaTo方法
@Override
public int compareTo(Student o) {
int result = this.age - o.age;
return result;
}
案例(二):
在上述案例(一)的基础上,进行改进
存储学生对象并遍历,创建TreeSet集合使用无参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
代码实现
上述其他的代码不变,只要修改comparaTo()中的内容
@Override
public int compareTo(Student o) {
int result = this.age - o.age;
result = result == 0 ? this.name.compareTo(o.getName()) : result;
return result;
}
关于comparaTo()方法在菜鸟编程上有这样子的解释
(2)按照指定的比较器排序-Comparator
- TreeSet的带参构造方法使用的就是比较器排序对元素进行排序
- 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compara(T o1,To2)方法。
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写。
案例分析:
依然使用上述案例的环境,使用Comparator的方式实现
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Demo03 {
public static void main(String[] args) {
TreeSet<Student> stu = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//o1表示现在要存入的那个元素
//o2表示已经存入到集合中的元素
//主要条件
int result = o1.getAge() - o2.getAge();
//次要条件
result = result == 0 ? o1.getName().compareTo(o2.getName()) :result;
return result;
}
});
Student s1 = new Student("小花花",27);
Student s2 = new Student("小花",29);
Student s3 = new Student("小明",25);
Student s4 = new Student("小黑",25);
stu.add(s1);
stu.add(s2);
stu.add(s3);
stu.add(s4);
System.out.println(stu);
}
}
两种比较方式小结
自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
两种方式中关于返回值的规则
如果返回值为负数,表示当前存入的元素是较小值,存左边
如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
如果返回值为正数,表示当前存入的元素是较大值,存右边
案例-比较字符串
按照长度排序,如果长度一样则按照首字母排序!
实现方式一:
public class Demo {
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int result = o1.length() - o2.length();
result = result == 0 ? o1.compareTo(o2) : result;
return result;
}
});
ts.add("d");
ts.add("asd");
ts.add("gre");
ts.add("zfsdfsf");
System.out.println(ts);
//[d, asd, gre, zfsdfsf]
}
}
实现方式二:
public class Demo {
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<>(
(String o1,String o2) -> {
int result = o1.length() - o2.length();
result = result == 0 ? o1.compareTo(o2) : result;
return result;
}
);
ts.add("d");
ts.add("asd");
ts.add("gre");
ts.add("zfsdfsf");
System.out.println(ts);
//[d, asd, gre, zfsdfsf]
}
}
Set集合的实现——HashSet
特点
-
底层数据结构是哈希表
-
存取无序
-
不可以存储重复元素
-
没有索引,不能使用普通for循环遍历
基本使用
public class Demo {
public static void main(String[] args) {
//创建集合对象
HashSet<String> set = new HashSet<String>();
//添加元素
set.add("hello");
set.add("world");
set.add("java");
//不包含重复元素的集合
set.add("world");
//遍历
for (String s : set) {
System.out.println(s);
//world
//java
//hello
}
}
}
哈希值
哈希值简介
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
如何获取哈希值
Object类中的public int hashCode():根据对象的地址值,返回计算出的哈希值
Coding
public class Student {
private String name;
private int 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;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class HashCodeDemo {
public static void main(String[] args) {
Student s1 = new Student("zhangsan",19);
Student s2 = new Student("xiaoming",78);
System.out.println(s1.hashCode());
//356573597
System.out.println(s2.hashCode());
//1735600054
System.out.println(s1.hashCode());
//356573597
}
}
上述代码演示,使用实例化Student对象s1,s2,它们调用hashCode()方法,根据创建的对象的地址返回它们对应的hash值。
我们可以查看IDEA中的hashCode的模板
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
当然我们可以对上述模板行进重写,就一般是根据对象的属性值来计算哈希值。此时,跟对象的地址值就没有关系。
哈希值的特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
哈希表结构
JDK1.8以前
JDK1.8之后
关于红黑树的详细介绍如下所示:
二叉树之红黑树https://blog.csdn.net/weixin_43715214/article/details/122811540
HashSet集合存储学生对象并遍历
案例需求
创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象
代码实现
学生类
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;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
测试类
public class HashSetDemo02 {
public static void main(String[] args) {
//创建HashSet集合对象
HashSet<Student> hs = new HashSet<Student>();
//创建学生对象
Student s1 = new Student("林青霞", 30);
Student s2 = new Student("张曼玉", 35);
Student s3 = new Student("王祖贤", 33);
Student s4 = new Student("王祖贤", 33);
//把学生添加到集合
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
//遍历集合(增强for)
for (Student s : hs) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)