Java三大Set集合全攻略:HashSet、TreeSet、LinkedHashSet核心方法+实战代码解析
文章目录
- Java三大Set集合全攻略:HashSet、TreeSet、LinkedHashSet核心方法+实战代码解析
- @[toc]
- **1. HashSet**
- **常用方法**
- **特点与区别**
- **使用场景**
- **代码示例**
- **2. TreeSet**
- **常用方法**
- **特点与区别**
- **使用场景**
- **代码示例**
- **3. LinkedHashSet**
- **常用方法**
- **特点与区别**
- **使用场景**
- **代码示例**
- **总结对比**
- **选择建议**
- 4.TreeSet<String> stree和Set<String> stree的区别
- **1. 声明方式的区别**
- **`TreeSet<String> stree;`**
- **`Set<String> stree;`**
- **2. 使用场景的区别**
- **使用 `TreeSet<String> stree;` 的场景**
- **使用 `Set<String> stree;` 的场景**
- **3. 示例代码对比**
- **示例 1:使用 `TreeSet<String>`**
- **示例 2:使用 `Set<String>`**
- **4. 总结与建议**
- **选择建议**
- **5. 面向接口编程的优势**
文章目录
- Java三大Set集合全攻略:HashSet、TreeSet、LinkedHashSet核心方法+实战代码解析
- @[toc]
- **1. HashSet**
- **常用方法**
- **特点与区别**
- **使用场景**
- **代码示例**
- **2. TreeSet**
- **常用方法**
- **特点与区别**
- **使用场景**
- **代码示例**
- **3. LinkedHashSet**
- **常用方法**
- **特点与区别**
- **使用场景**
- **代码示例**
- **总结对比**
- **选择建议**
- 4.TreeSet<String> stree和Set<String> stree的区别
- **1. 声明方式的区别**
- **`TreeSet<String> stree;`**
- **`Set<String> stree;`**
- **2. 使用场景的区别**
- **使用 `TreeSet<String> stree;` 的场景**
- **使用 `Set<String> stree;` 的场景**
- **3. 示例代码对比**
- **示例 1:使用 `TreeSet<String>`**
- **示例 2:使用 `Set<String>`**
- **4. 总结与建议**
- **选择建议**
- **5. 面向接口编程的优势**
1. HashSet
常用方法
add(E e):添加元素。remove(Object o):删除指定元素。contains(Object o):判断是否包含指定元素。size():返回集合的大小。isEmpty():判断集合是否为空。clear():清空集合。iterator():返回一个迭代器,用于遍历集合。
特点与区别
- 无序:元素的存储顺序由哈希值决定,不保证插入顺序。
- 不允许重复元素:通过
hashCode()和equals()方法判断重复。 - 允许一个
null元素。 - 性能:查询和插入效率高(时间复杂度接近 O(1))。
使用场景
- 快速去重。
- 判断某个元素是否存在。
- 不需要关心元素顺序。
代码示例
import java.util.HashSet; public class HashSetExample {public static void main(String[] args) {HashSet<String> set = new HashSet<>();// 添加元素set.add("Apple");set.add("Banana");set.add("Orange");set.add("Apple"); // 重复元素不会被添加System.out.println("HashSet 内容:" + set);// 判断是否包含元素System.out.println("是否包含 'Banana':" + set.contains("Banana"));// 删除元素set.remove("Banana");System.out.println("删除 'Banana' 后的内容:" + set);// 遍历集合for (String fruit : set) {System.out.println("遍历元素:" + fruit);}// 获取集合大小System.out.println("集合大小:" + set.size());// 清空集合set.clear();System.out.println("清空后是否为空:" + set.isEmpty());} }java运行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
2. TreeSet
常用方法
add(E e):添加元素。remove(Object o):删除指定元素。contains(Object o):判断是否包含指定元素。size():返回集合的大小。isEmpty():判断集合是否为空。clear():清空集合。first():返回集合中最小的元素。last():返回集合中最大的元素。subSet(E fromElement, E toElement):返回子集(范围查询)。headSet(E toElement):返回小于指定元素的子集。tailSet(E fromElement):返回大于等于指定元素的子集。
特点与区别
- 有序:元素按照自然顺序或自定义
Comparator排序。 - 不允许重复元素。
- 不允许
null元素:因为排序需要比较,null无法参与比较。 - 性能:插入、删除和查找的时间复杂度为 O(log n)。
使用场景
- 需要对集合中的元素进行排序。
- 需要高效的范围查询(如获取某个区间内的元素)。
代码示例
import java.util.TreeSet; public class TreeSetExample {public static void main(String[] args) {TreeSet<Integer> set = new TreeSet<>();// 添加元素set.add(5);set.add(1);set.add(3);set.add(5); // 重复元素不会被添加System.out.println("TreeSet 内容:" + set);// 获取最小值和最大值System.out.println("最小值:" + set.first());System.out.println("最大值:" + set.last());// 范围查询System.out.println("范围 [1, 5):" + set.subSet(1, 5));System.out.println("小于 3 的子集:" + set.headSet(3));System.out.println("大于等于 3 的子集:" + set.tailSet(3));// 删除元素set.remove(3);System.out.println("删除 3 后的内容:" + set);// 遍历集合for (Integer num : set) {System.out.println("遍历元素:" + num);}// 清空集合set.clear();System.out.println("清空后是否为空:" + set.isEmpty());} }java运行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
3. LinkedHashSet
常用方法
add(E e):添加元素。remove(Object o):删除指定元素。contains(Object o):判断是否包含指定元素。size():返回集合的大小。isEmpty():判断集合是否为空。clear():清空集合。iterator():返回一个迭代器,用于遍历集合。
特点与区别
- 有序:元素的迭代顺序与插入顺序一致。
- 不允许重复元素。
- 允许一个
null元素。 - 性能:插入和删除性能略低于
HashSet,但仍接近 O(1)。
使用场景
- 需要去重,同时保持插入顺序。
代码示例
import java.util.LinkedHashSet; public class LinkedHashSetExample {public static void main(String[] args) {LinkedHashSet<String> set = new LinkedHashSet<>();// 添加元素set.add("Apple");set.add("Banana");set.add("Orange");set.add("Apple"); // 重复元素不会被添加System.out.println("LinkedHashSet 内容:" + set);// 判断是否包含元素System.out.println("是否包含 'Banana':" + set.contains("Banana"));// 删除元素set.remove("Banana");System.out.println("删除 'Banana' 后的内容:" + set);// 遍历集合for (String fruit : set) {System.out.println("遍历元素:" + fruit);}// 获取集合大小System.out.println("集合大小:" + set.size());// 清空集合set.clear();System.out.println("清空后是否为空:" + set.isEmpty());} }java运行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
总结对比
| 特性 | HashSet | TreeSet | LinkedHashSet |
|---|---|---|---|
| 底层实现 | 哈希表 | 红黑树 | 哈希表 + 双向链表 |
| 是否有序 | 无序 | 自然顺序或自定义顺序 | 插入顺序 |
| 是否允许重复 | 不允许 | 不允许 | 不允许 |
是否允许 null | 是 | 否 | 是 |
| 时间复杂度 | 插入/查询:O(1) | 插入/查询:O(log n) | 插入/查询:接近 O(1) |
| 适用场景 | 快速去重、判断存在性 | 排序、范围查询 | 去重并保持插入顺序 |
选择建议
- 如果只需要快速去重且不关心顺序,优先选择
HashSet。 - 如果需要对集合元素进行排序或范围查询,选择
TreeSet。 - 如果需要去重的同时保留插入顺序,选择
LinkedHashSet。
4.TreeSet stree和Set stree的区别
在 Java 中,TreeSet<String> stree; 和 Set<String> stree; 的区别主要体现在类型声明的层次和代码的灵活性上。以下是详细的对比和解释:
1. 声明方式的区别
TreeSet<String> stree;
- 具体实现类:
TreeSet是一个具体的集合类,属于java.util包中的实现类。 - 强绑定:变量
stree被明确声明为TreeSet类型,只能引用TreeSet或其子类的对象。 - 直接调用方法:可以直接调用
TreeSet特有的方法(如first()、last()、subSet()等)。
Set<String> stree;
- 接口类型:
Set是一个接口,定义了集合的基本行为(如add()、remove()、contains()等)。 - 弱绑定:变量
stree被声明为Set接口类型,可以引用任何实现了Set接口的类的对象(如HashSet、TreeSet、LinkedHashSet等)。 - 限制调用方法:只能调用
Set接口中定义的方法,不能直接调用实现类特有的方法(如TreeSet的first()或subSet())。
2. 使用场景的区别
使用 TreeSet<String> stree; 的场景
- 当你明确需要使用
TreeSet的特性时,例如:- 元素需要按自然顺序或自定义顺序排序。
- 需要使用
TreeSet提供的范围查询方法(如subSet()、headSet()、tailSet())。 - 需要获取集合中的最小值(
first())或最大值(last())。
使用 Set<String> stree; 的场景
- 当你希望代码更具通用性和灵活性时,例如:
- 不关心具体的实现类,只需要一个满足
Set接口功能的集合。 - 可能会根据需求切换不同的实现类(如从
TreeSet切换到HashSet或LinkedHashSet),而无需修改大量代码。 - 符合面向接口编程的原则,提升代码的可维护性和扩展性。
- 不关心具体的实现类,只需要一个满足
3. 示例代码对比
示例 1:使用 TreeSet<String>
import java.util.TreeSet; public class TreeSetExample {public static void main(String[] args) {// 声明为 TreeSet 类型TreeSet<String> stree = new TreeSet<>();// 添加元素stree.add("Apple");stree.add("Banana");stree.add("Orange");// 直接调用 TreeSet 特有的方法System.out.println("第一个元素:" + stree.first()); // 输出:AppleSystem.out.println("最后一个元素:" + stree.last()); // 输出:OrangeSystem.out.println("范围 [Apple, Orange):" + stree.subSet("Apple", "Orange")); // 输出:[Apple, Banana]} }java运行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
示例 2:使用 Set<String>
import java.util.Set; import java.util.TreeSet; public class SetExample {public static void main(String[] args) {// 声明为 Set 类型Set<String> stree = new TreeSet<>();// 添加元素stree.add("Apple");stree.add("Banana");stree.add("Orange");// 只能调用 Set 接口定义的方法System.out.println("集合内容:" + stree); // 输出:[Apple, Banana, Orange]// 无法直接调用 TreeSet 特有的方法// System.out.println(stree.first()); // 编译错误} }java运行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
4. 总结与建议
| 特性 | TreeSet<String> stree; | Set<String> stree; |
|---|---|---|
| 类型声明 | 具体实现类(TreeSet) | 接口类型(Set) |
| 方法调用 | 可以调用 TreeSet 特有的方法 | 只能调用 Set 接口中定义的方法 |
| 灵活性 | 强绑定,不够灵活 | 弱绑定,更加灵活 |
| 适用场景 | 明确需要使用 TreeSet 的特有功能 | 面向接口编程,不关心具体实现类 |
选择建议
- 如果你需要使用
TreeSet的特定功能(如排序、范围查询等),直接声明为TreeSet<String>更方便。 - 如果你希望代码更具通用性,并且可能需要切换实现类,建议使用
Set<String>,并在初始化时指定具体的实现类(如new TreeSet<>())。
5. 面向接口编程的优势
使用 Set<String> 声明变量是一种面向接口编程的方式,具有以下优势:
- 解耦:代码与具体实现类解耦,便于后期更换实现类。
- 可扩展性:如果未来需要将
TreeSet替换为HashSet或其他实现类,只需修改初始化部分即可。 - 代码复用性:通过接口定义行为,使得代码更易于复用和测试。
示例:
// 使用 Set 接口声明变量 Set<String> set; // 根据需求选择不同的实现类 set = new HashSet<>(); // 使用 HashSet set = new TreeSet<>(); // 使用 TreeSet set = new LinkedHashSet<>(); // 使用 LinkedHashSetjava运行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这种方式让代码更加灵活,符合良好的设计原则。
:如果未来需要将 TreeSet 替换为 HashSet 或其他实现类,只需修改初始化部分即可。
3. 代码复用性:通过接口定义行为,使得代码更易于复用和测试。
示例:
// 使用 Set 接口声明变量 Set<String> set; // 根据需求选择不同的实现类 set = new HashSet<>(); // 使用 HashSet set = new TreeSet<>(); // 使用 TreeSet set = new LinkedHashSet<>(); // 使用 LinkedHashSetjava运行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这种方式让代码更加灵活,符合良好的设计原则。
