Set集合语法详解
一、Set系列集合
- 无序 : 存取顺序不一致
- 不重复 : 可以去除重复
- 无索引 : 没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
二、Set集合的实现类
- HashSet : 无序、不重复、无索引
- LinkedHashSet : 有序、不重复、无索引
- TreeSet : 可排序、不重复、无锁引
✏️ 注意
Set接口中的方法上基本与Collection的API一致
collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的
| public boolean add(E e) | 把给定对象添加到当前集合中 |
| public void clear( ) | 清空集合中的所有元素 |
| public boolean remove(E e) | 把给定的对象在当前集合中删除 |
| public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象,底层是通过equals实现的,如果存自定义对象,没有重写equals方法,那么默认使用Object类中的equals方法进行判断,依赖地址值进行判断 |
| public boolean isEmpty( ) | 判断当前集合是否为空 |
| public int size( ) | 返回集合中元素的个数/集合的长度 |
⚠️ Set的遍历
1.增强for
2.迭代器
3.Lambda表达式
Set<String> set = new HashSet<>();
set.add("甜甜");
set.add("源源");
set.add("名侦探柯南");
set.add("怪盗基德");
// 1.增强for
for (String s : set) {
System.out.println(s);
}
// 2.迭代器
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()){
String s = iterator.next();
System.out.println(s);
}
// 3.Lambda表达式
set.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
set.forEach(s -> System.out.println(s));
1. HashSet
1.1 Hash表
- HashSet集合底层采取哈希表存储数据
- 哈希表是一种对于增删改查数据性能都较好的结构
💡 哈希表的组成
JDK8之前 : 数组 + 链表
JDK8开始 : 数组 + 链表 + 红黑树
int index = (数组长度 – 1) & 哈希值 —>计算位置
哈希值 :
- 根据hashCode方法计算出来的int类型的整数
- 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
- 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
对象的哈希值特点:
- 如果没有重写hashCode方法,不同对象计算出来的哈希值是不同的
- 如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
- 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)
1.1.1 HashCode的代码演示
public class Student {
private String name;
private Integer age;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) && Objects.equals(age, student.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Student s1 = new Student("甜甜",18);
Student s2 = new Student("甜甜",18);
System.out.println(s1.hashCode()); //29741139
System.out.println(s2.hashCode()); //29741139
//哈希碰撞,小概率事件
System.out.println("abc".hashCode()); //96354
System.out.println("acD".hashCode()); //96354
1.2 HashSet 底层原理 (hashSet.html 原理演示网页,点击学习吧!)
-
一样 : 不存(重复元素)
-
不一样 : 存入数组,形成链表
JDK8以前 : 新元素存入数组,老元素挂在新元素下面
JDK8以后 : 新元素直接挂在老元素下面,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树
⚠️ 注意
如果集合中存储的是自定义对象,必须要重写hashCode和equals方法
除自定义对象外,Java底层已经实现了 String Integer
遍历时按照hash数组从0索引开始遍历,有链表就把链表遍历完,再遍历下一个索引,循环往复
1.3 LinkedHashSet底层原理(LinkedHashSet.html 原理演示网页,点击学习吧!)
- 有序、不重复、无索引
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理 : 底层数据结构依然是哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序
public static void main(String[] args) {
Student s1 = new Student("甜甜",18);
Student s2 = new Student("柯南",19);
Student s3 = new Student("源源",18);
Student s4 = new Student("灰原哀",18);
Student s5 = new Student("元太",18);
Student s6 = new Student("甜甜",18);
LinkedHashSet<Student> lhs = new LinkedHashSet<>();
System.out.println(lhs.add(s1));//ture
System.out.println(lhs.add(s2));//ture
System.out.println(lhs.add(s3));//ture
System.out.println(lhs.add(s4));//ture
System.out.println(lhs.add(s5));//ture
System.out.println(lhs.add(s6));//false
System.out.println(lhs);//[Student{name='甜甜', age=18}, Student{name='柯南', age=19}, Student{name='源源', age=18}, Student{name='灰原哀', age=18}, Student{name='元太', age=18}]
}
1.4 TreeSet
1.4.1 TreeSet的特点 :
- 不重复、无索引、可排序
- 可排序 : 按照元素的默认规则(由大到小)排序
- TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好
💡 TreeSet集合默认的规则
- 对于数值类型 : Integer、Double,默认按照从小到大的顺序进行排序
- 对于字符、字符串类型 : 按照字符在ASCII表中的数字升序进行排序
1.4.2 第一种排序规则(默认排序/自然排序 : Javabean类实现Comparable接口指定比较规则)
public class Student implements Comparable<Student>{
@Override
public int compareTo(Student o) {
//指定排序的规则
//只看年龄,我想要按照年龄的升序进行排列
return this.getAge() – o.getAge();
}
}
💡 提示
this : 表示当前要添加的元素
o : 表示已经在红黑树存在的元素
返回值 :
- 负数 : 认为要添加的元素是小的,存在左边
- 整数 : 认为要添加的元素是大的,存在右边
- 0 : 认为要添加的元素已经存在,舍弃
1.4.3 第二种排序规则(比较器排序 : 创建TreeSet对象的时候,传递比较器Comparator指定规则)
使用规则 : 默认使用第一种,如果第一种不能满足当前需求,就使用第二种
- 存入四个字符串, “c” , “ab” , “df” , “qwer” ,按照长度排序,如果一样则按照首字母排序
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// o1:表示当前要添加的元素
// o2:表示已经在红黑树存在的元素
//返回值规则和之前一样
//按照长度排序
int i = o1.length() – o2.length();
//如果一样再按照首字母排序
i = i == 0 ? o1.compareTo(o2) : i;
return i;
}
});
//Lambda表达式
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("ab");
ts.add("df");
ts.add("qwer");
System.out.println(ts); //输出:a,ab,df,qwer
}
三、总结
如果想要集合中的元素可重复
用ArrayList集合,基于数组的
如果想要集合中的元素可重复,而且当前的增删操作明显多于查询
用LinkedList集合,基于链表的
如果想对集合中的元素去重
用HashSet集合,基于哈希表的
如果想对集合中的元素去重,而且保证存取顺序
用LinkedHashSet集合,基于哈希表和双向链表,效率低于HashSet
如果想对集合中的元素进行排序
用TreeSet集合,基于红黑树,后续也可以使用List集合实现排序
网硕互联帮助中心

评论前必须登录!
注册