单列集合---Set系列集合

Set是单列集合中另一个大的门派;它表示一个不允许重复元素且不保证顺序的集合;

以下是关于 Set 的详细语法和使用说明:


一、Set 的核心特性

  1. 唯一性:不允许重复元素(必须重写equals()hashCode() 方法,因为数据的存储,获取和判断是否一样都依赖这俩方法,不过ideal自动生成的重写足够用)
  2. 无序性:默认不保证元素的存储顺序(具体取决于实现类)。
  3. 允许 null 元素:大多数实现类允许一个 null 值(但 TreeSet 不允许)。

二、常用实现类

类名

描述

HashSet

基于哈希表实现,无序,查询效率高(O(1)),允许一个 null

LinkedHashSet

基于哈希表和链表,维护插入顺序,查询效率略低于 HashSet

TreeSet

基于红黑树实现元素按自然顺序或自定义 Comparator 排序,不允许 null

一定要了解哈希表的相关,这个并不难,课下可搜集一大堆资料,主要是这玩意整理成文字远远不如看视频来得简单;

哈希表的原理大概就是通过hashCode() 方法算出元素的应该存放到哈希表的哪个位置(所以以哈希表为底层数据结构的集合类的查询效率也很高,因为他是直接算出位置的),如果哈希值相同就调用equals()判断,如果俩都一样就判定元素相同,至于“哈希碰撞”其实就是俩不同的哈希值一样;

如果是基于各种树的集合类,基本就是,数据是排序存储的;


三、基础语法与操作

1. 初始化 Set
// 使用实现类初始化(推荐指定泛型类型)
Set<String> hashSet = new HashSet<>();
Set<Integer> linkedHashSet = new LinkedHashSet<>();
Set<Double> treeSet = new TreeSet<>();
2. 常用方法
Set<String> set = new HashSet<>();

// 添加元素
set.add("Apple");      // 成功返回 true,重复元素返回 false
set.addAll(Arrays.asList("Banana", "Cherry")); // 批量添加

// 删除元素
set.remove("Apple");   // 存在则删除并返回 true
set.clear();           // 清空集合

// 查询操作
boolean exists = set.contains("Banana"); // 检查是否存在
int size = set.size();                   // 获取元素数量
boolean isEmpty = set.isEmpty();         // 判断是否为空

// 遍历集合
for (String element : set) {
    System.out.println(element);
}

// 使用迭代器
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

//lambda表达式
set.forEach(element -> System.out.println(element));

三、HashSet相关

1、HashSet 的核心特性
    1. 唯一性:不允许重复元素(依赖 equals()hashCode() 方法判断重复)。
    2. 无序性:不保证元素的存储或遍历顺序(基于哈希表实现)。
    3. 允许 null 元素:支持存储一个 null 值。
    4. 非线程安全:多线程环境下需手动同步或使用并发容器。
    5. 高性能:插入、删除、查询操作平均时间复杂度为 O(1)。
    6. 去重是HashCode天然的能力,就凭这个就可以使用在很多地方;

注意:在HashSet集合中,数据在存储或者验证唯一性时,很重要的关键是“哈希值”;因此,处于原理考虑,如果HashSet集合中存储的元素是对象类型,就一定要重写hashCode()和equals()方法;因为HashSet底层的实现逻辑是依靠哈希表数据结构,无论是处于判断数据添加时的存储位置还是判断两个元素是否相等,重写这俩方法都异常的重要;通常是俩方法都相等才算相等;

2、初始化 HashSet
2.1. 基础初始化
// 默认构造:初始容量16,加载因子0.75
Set<String> set1 = new HashSet<>(); 

// 指定初始容量(减少扩容次数)
Set<Integer> set2 = new HashSet<>(32); 

// 指定初始容量和加载因子(默认0.75)
Set<Double> set3 = new HashSet<>(64, 0.5f); 

// 通过已有集合初始化
List<String> list = Arrays.asList("A", "B", "C");
Set<String> set4 = new HashSet<>(list);

HashCode底层原理利用的是哈希表,因此,对于HashCode的初始化基本都是对哈希表的初始化;
初始容量:哈希表的总容量
加载因子:当哈希表的位置被占75%后,哈希表扩容两倍

3、常用方法
3.1. 元素操作
HashSet<String> set = new HashSet<>();

// 添加元素
set.add("Apple");       // 成功返回true,重复返回false
set.addAll(List.of("Banana", "Cherry")); // 批量添加

// 删除元素
set.remove("Apple");    // 存在则删除并返回true
set.removeAll(List.of("Banana")); // 批量删除
set.clear();            // 清空集合

// 检查元素
boolean exists = set.contains("Cherry"); // true
boolean isEmpty = set.isEmpty();        // false
int size = set.size();                  // 当前元素数量
3.2. 遍历方式

// for-each 循环
for (String element : set) {
    System.out.println(element);
}

// 迭代器
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

// Java 8+ Stream API
set.stream().forEach(System.out::println);

四、LinkedHashSet相关

作为HashSet的子类功能基本继承HashSet,唯一不同的是底层的实现逻辑在哈希表的基础上添加了双向链表;使得LinkedHashSet支持有序,即读取的顺序和存储的顺序一致的特性;

五、TreeSet相关

其实关于Set接口下面的几个实现类的方法都差不多,现在主要的问题就是他们各自的特点,因此如何使用不会着重讲,主要是他们的特性和不同;而TreeSet最大的特点就是,凡事他的元素都会自动排序;

注意:TreeSet的底层是红黑树,因此不需要重写HashCode()和equals()方法;

但作为Set集合的一员,他们家族的老本行---“唯一性”,TreeSet是肯定存在的,那他是如何做到的呢?

主要依赖于元素的比较逻辑(compareTocompare方法)而非equals方法,如果是自定义的类型没有重写compareTo方法就会报错;

1、TreeSet 的核心特性
      • 有序性:元素按自然顺序(Comparable)或自定义规则(Comparator)自动排序;
      • 唯一性:不允许重复元素(依赖 equals() 和 compareTo()/compare() 方法);
      • 禁止 null 元素:尝试添加 null 会抛出 NullPointerException;
      • 基于红黑树:底层通过平衡二叉搜索树实现,操作时间复杂度为 O(log n);
      • 非线程安全:需手动同步或使用并发容器(如 ConcurrentSkipListSet)。
2、常用方法与操作
2.1. 基本操作
TreeSet<String> set = new TreeSet<>();

// 添加元素
set.add("Apple");          // 成功返回 true
set.addAll(List.of("Banana", "Cherry"));

// 删除元素
set.remove("Apple");       // 存在则返回 true
set.pollFirst();           // 删除并返回第一个元素
set.pollLast();            // 删除并返回最后一个元素
2.2. 查询操作
// 获取极值
String first = set.first();    // 第一个元素
String last = set.last();      // 最后一个元素

// 范围查询
String lower = set.lower("Banana");   // 小于指定元素的最大元素
String higher = set.higher("Banana"); // 大于指定元素的最小元素

// 检查元素
boolean contains = set.contains("Cherry");
int size = set.size();
boolean isEmpty = set.isEmpty();
2.3. 子集操作
// 获取 [fromElement, toElement) 范围的子集
SortedSet<String> subset = set.subSet("Banana", "Cherry");

// 获取小于指定元素的子集
SortedSet<String> headSet = set.headSet("Cherry");

// 获取大于等于指定元素的子集
SortedSet<String> tailSet = set.tailSet("Banana");
2.4、遍历方式
// 自然顺序遍历
for (String element : set) {
    System.out.println(element);
}

// 降序遍历(通过 descendingIterator)
Iterator<String> descIterator = set.descendingIterator();
while (descIterator.hasNext()) {
    System.out.println(descIterator.next());
}

// Java 8+ Stream API
set.stream().forEach(System.out::println);
3、排序操作(以下代码请多注意注释内容,注释内容解释了大部分原理,看似复杂其实还行)
3.1. 自然排序(如果是自定义类需实现 Comparable 接口)
// 默认自然排序(如 Integer、String 已实现 Comparable)
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(5);
numbers.add(1);
System.out.println(numbers); // 输出 [1, 5]

对于自定义的类,需要自己去书写一定的规则;

// 自定义对象需实现 Comparable
class Student implements Comparable<Student> {
    String name;
    int age;
    @Override
    public int compareTo(Student o) {
        return this.getAge() - o.getAge();
    }
// this: 表示当前要添加的元素
// o: 表示已经在红黑树存在的元素
// 返回值:
// 负数: 认为要添加的元素是小的,存右边
// 正数: 认为要添加的元素是大的,存左边
// 0: 认为要添加的元素已经存在,舍弃
}
TreeSet<Student> students = new TreeSet<>();
3.2. 自定义排序(通过 Comparator

默认情况下第一种排序已经够用了,第二种其实就是在初始化TressSet时传入一个规则,使得这个规则可以覆盖原来的规则,在不改变原类型源码的情况下,更灵活适应各种情况;

形式不重要,仔细品味,其实就是把第一种重写的compareTo给单独拎了出来而已;

//o1表示当前要添加的元素
//o2表示已经在红黑树存在的元素
//返回值规则跟之前是一样的
TreeSet<String> ts = new TreeSet<>((o1, o2) -> {
    // 按照长度排序
    int i = o1.length() - o2.length();
    // 如果一样长则按照首字母排序
    i = i == 0 ? o1.compareTo(o2) : i;
    return i;
});

reverseSet.add("Apple");
reverseSet.add("Banana");
System.out.println(reverseSet); // 输出 [Banana, Apple]

// 使用 Comparator 对象
Comparator<Student> ageComparator = Comparator.comparingInt(s -> s.age);
TreeSet<Student> studentsByAge = new TreeSet<>(ageComparator);

六、特殊操作与注意事项

一定要注意,虽然都是Set系列下的集合,但是他们实现唯一性和存储的方式不完全一样,一定要注意区分,这是重点;

1. 元素唯一性实现原理【HashSet和TreeHashSet集合的原理】
  • 当向 Set 添加对象时,会调用对象的 hashCode() 方法计算哈希值。
  • 如果哈希值冲突,再通过 equals() 方法比较内容是否相同。
  • 重要:自定义对象作为元素时,必须重写 hashCode()equals() 方法!
2. 排序与比较【TreeSet 集合实现唯一性和存储的原理】
  • 自然排序TreeSet 要求元素实现 Comparable 接口。
Set<Integer> sortedSet = new TreeSet<>(Arrays.asList(3, 1, 2)); // 自动排序为 [1, 2, 3]
  • 自定义排序:通过 Comparator 指定排序规则。
Set<String> customSorted = new TreeSet<>((a, b) -> b.compareTo(a)); // 降序排列
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值