Set接口的子类
【Java基础】集合框架
【Java基础】Collection子接口:Set
HashSet
HashSet的特点
- 底层数据结构是哈希表
- 是一个元素为链表的数组
- 对集合的迭代顺序不作任何保证
- 不保证元素的存储和取出顺序一致
- 元素唯一
- 依赖hashCode()和equals()方法
如何保证元素唯一性的呢?
HashSet集合添加一个元素的过程:
- 首先比较哈希值是否相同
- 相同:继续执行equals()方法
- 返回true:说明元素重复就不添加
- 返回false:说明元素不重复就添加到集合
- 不同:就直接把元素添加到集合
- 相同:继续执行equals()方法
HashSet的案例
1、存储字符串并遍历
问题: 为什么存储字符串的时候,字符串内容相同的只存储了一个呢?
原因: HashSet底层依赖hashCode()和equals()方法,String类重写了hashCode()和equals()方法,可以把内容相同的字符串去掉只留一个;
import java.util.HashSet;
public class HashSetDemo1 {
public static void main(String[] args) {
//创建集合对象
HashSet<String> hs = new HashSet<String>();
//把字符串添加到集合
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
//遍历集合
for (String s : hs) {
System.out.println(s);
}
}
}
/*
world
java
hello
*/
2、存储自定义对象并遍历
要求: 如果两个对象的成员变量值都相同,则为同一个元素;
问题: 目前是不符合要求的,因为HashSet底层依赖hashCode()和equals()方法,而学生类中没有重写这两个方法,默认使用的是Object类的,此时他们的哈希值是不会一样的,根本就不会继续判断,就直接把元素添加到集合。
解决方案: 重写学生类中的hashCode()和equals()方法,自动生成即可;
//定义学生类Student-->省略
//测试类:
import java.util.HashSet;
class HashSetDemo2 {
public static void main(String[] args) {
//创建集合对象
HashSet<Student> hs = new HashSet<Student>();
//创建学生对象
Student s1 = new Student("张三", 18);
Student s2 = new Student("李四", 20);
Student s3 = new Student("王五", 22);
Student s4 = new Student("赵六", 24);
Student s1 = new Student("张三", 18);
//把学生添加到集合
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
for (Student s : hs) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
HashSet集合的使用步骤
- 定义自定义对象类
- 创建集合对象
- 创建元素对象
- 把元素添加到集合
- 遍历集合
- 重写自定义对象类中的hashCode()和equals()方法(自动生成即可)
LinkedHashSet
LinkedHashSet的特点
- 底层数据结构是链表和哈希表
- 具有可预测的迭代次序
- 元素的存储和取出顺序一致的
- 元素有序唯一
- 由链表保证元素有序
- 由哈希表保证元素唯一性
TreeSet
TreeSet的特点
- 底层数据结构是红黑树
- 是一个自平衡的二叉树
- 能够对元素按照一定的规则进行排序
- 元素排序
- 自然排序
- 比较器排序
- 元素唯一
- 根据比较的返回值是否是0来决定
如何保证元素排序的呢?
- 使用元素的自然顺序对元素进行排序(自然排序)
- 根据创建set时提供的Comparator进行排序(比较器排序)
- 具体取决于使用的构造方法
1、自然排序
- TreeSet()
- 构造一个新的,空的树组,根据其元素的自然排序进行排序;
- 让元素所属的类实现Comparable接口,重写compareTo(T o)方法;
- 比较两个对象是否相同的标准为:compareTo()返回0
2、比较器排序
- TreeSet(Comparator<? super E> comparator)
- 构造一个新的,空的树集,根据指定的比较器进行排序;
- 让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法;
- 比较两个对象是否相同的标准为:compare()返回0
TreeSet的案例
1、存储Integer对象并遍历
import java.util.TreeSet;
public class TreeSetDemo1 {
public static void main(String[] args) {
//创建集合对象
//自然顺序进行排序
TreeSet<Integer> ts = new TreeSet<Integer>();
//创建元素并添加到集合
ts.add(20); //自动装箱
ts.add(18);
ts.add(23);
ts.add(19);
ts.add(18);
//遍历集合
for (Integer i : ts) {
System.out.println(i);
}
}
}
2、存储自定义对象并遍历
要求: 按照姓名的长度排序
2.1、使用自然排序
- 存储学生对象并遍历, 创建TreeSet集合使用无参构造方法;
- 用TreeSet集合存储自定义对象, 无参构造方法使用的是自然排序对元素进行排序的;
- 就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法;
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写;
学生类:
//实现自然排序接口
public class Student implements Comparable<Student> {
//成员变量:name age -->省略
//构造方法:无参 带参 -->省略
//成员方法:getXxx() setXxx() -->省略
@Override
public int compareTo(Student s) {
//主要条件:姓名的长度
int num = this.name.length() - s.name.length();
//次要条件:姓名的内容
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
//次要条件:年龄
int num3 = num2 == 0 ? this.age - s.age : num2;
return num3;
}
}
测试类:
import java.util.TreeSet;
public class TreeSetDemo2 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<Student>();
//创建学生对象
Student s1 = new Student("lisi", 20);
Student s2 = new Student("zhangsan", 18);
Student s3 = new Student("zhaoliu", 24);
Student s4 = new Student("wangwu", 20);
Student s5 = new Student("lisi", 22);
Student s6 = new Student("zhangsan", 18);
//把学生添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
//遍历集合
for (Student s : ts) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
2.2、使用比较器排序
- 存储学生对象并遍历, 创建TreeSet集合使用带参构造方法;
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的;
- 就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法;
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写;
//定义学生类Student-->省略
//测试类:
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo2 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//主要条件:姓名的长度
int num = s1.getName().length() - s2.getName().length();
//次要条件:姓名的内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
//次要条件:年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
});
//创建学生对象
Student s1 = new Student("lisi", 20);
Student s2 = new Student("zhangsan", 18);
Student s3 = new Student("zhaoliu", 24);
Student s4 = new Student("wangwu", 20);
Student s5 = new Student("lisi", 22);
Student s6 = new Student("zhangsan", 18);
//把学生添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
//遍历集合
for (Student s : ts) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}