当前位置: 首页 > news >正文

Java三大Set集合全攻略:HashSet、TreeSet、LinkedHashSet核心方法+实战代码解析

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. 面向接口编程的优势**

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

总结对比

特性HashSetTreeSetLinkedHashSet
底层实现哈希表红黑树哈希表 + 双向链表
是否有序无序自然顺序或自定义顺序插入顺序
是否允许重复不允许不允许不允许
是否允许 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 接口的类的对象(如 HashSetTreeSetLinkedHashSet 等)。
  • 限制调用方法:只能调用 Set 接口中定义的方法,不能直接调用实现类特有的方法(如 TreeSetfirst()subSet())。

2. 使用场景的区别
使用 TreeSet<String> stree; 的场景
  • 当你明确需要使用 TreeSet 的特性时,例如:
    • 元素需要按自然顺序或自定义顺序排序。
    • 需要使用 TreeSet 提供的范围查询方法(如 subSet()headSet()tailSet())。
    • 需要获取集合中的最小值(first())或最大值(last())。
使用 Set<String> stree; 的场景
  • 当你希望代码更具通用性和灵活性时,例如:
    • 不关心具体的实现类,只需要一个满足 Set 接口功能的集合。
    • 可能会根据需求切换不同的实现类(如从 TreeSet 切换到 HashSetLinkedHashSet),而无需修改大量代码。
    • 符合面向接口编程的原则,提升代码的可维护性和扩展性。

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> 声明变量是一种面向接口编程的方式,具有以下优势:

  1. 解耦:代码与具体实现类解耦,便于后期更换实现类。
  2. 可扩展性:如果未来需要将 TreeSet 替换为 HashSet 或其他实现类,只需修改初始化部分即可。
  3. 代码复用性:通过接口定义行为,使得代码更易于复用和测试。

示例:

// 使用 Set 接口声明变量
Set<String> set;
// 根据需求选择不同的实现类
set = new HashSet<>(); // 使用 HashSet
set = new TreeSet<>(); // 使用 TreeSet
set = new LinkedHashSet<>(); // 使用 LinkedHashSet
java
运行
  • 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<>(); // 使用 LinkedHashSet
java
运行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这种方式让代码更加灵活,符合良好的设计原则。