Java Set

Java 集合 - Set 详解

集合(Set)是用于存储和处理无重复元素的高效数据结构,映射表(Map)则类似目录,支持通过键值快速查询和获取对应值。例如检验某人是否在论文答辩名单中,用 Set 实现比线性表更高效;若需存储学生详细信息(如性别、身高、住址),可将名字作为键值,通过 Map 快速获取,Map 是这类场景的理想数据结构。

Set 体系结构

Set 接口扩展自 java.util.Collection<E> 接口,未引入新方法或常量,仅规定实例不包含重复元素。实现 Set 接口的具体类需确保不添加重复元素,核心实现类有 HashSet、LinkedHashSet 和 TreeSet。
image

Set 核心实现类详解

HashSet

HashSet 用于存储互不相同的元素,追求高效存储与访问。添加到 HashSet 中的对象需正确实现 hashCode 方法,确保散列码分散,减少哈希冲突。

核心特性

  • 元素无序且唯一,基于哈希表实现。
  • 若两个对象相等(equals 方法返回 true),其散列码必须相同;不相等对象可能有相同散列码,因此应该重写 hashCode 方法以避免出现太多这样的情况。
  • Java API 中大多数类已实现 hashCode 方法,可直接使用。

构造方法

  1. HashSet():构造空散列集。
  2. HashSet(Collection<? extends E> elements):构造散列集并添加指定集合的所有元素。
  3. HashSet(int initialCapacity):构造指定容量(桶数)的空散列集。
  4. HashSet(int initialCapacity, float loadFactor):构造指定容量和装填因子的空散列集。装填因子是散列表填充百分比阈值(0.0~1.0),默认 0.75,是时间开销与空间开销的平衡值,超过阈值会触发再散列。

示例代码

HashSetDemo

package com.set;

import java.util.HashSet;
import java.util.Set;

/**
 * @author Jing61
 */
public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("hello world1");
        set.add("hello world2");
        set.add("hello world3");
        set.add("hello world2");

        System.out.println(set); // 插入无序
        System.out.println(set.size()); // 3

        Set<Circle> set1 = new HashSet<>();
        set1.add(new Circle(1));
        set1.add(new Circle(2));
        set1.add(new Circle(3));
        set1.add(new Circle(3));

        System.out.println(set1);
    }
}

Circle

package com.set;

import java.util.Objects;

/**
 * @author Jing61
 */
public class Circle {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    public double findArea() {
        return Math.PI * radius * radius;
    }

    @Override
    public String toString() {
        return "Circle[radius=" + radius + "]";
    }

    /**
     * 如果圆半径相等,圆对象相等
     * jdk规定:如果两个对象相等,那么它们的hashcode值一定相等
     * 两个不相等的对象可能存在相同的hashcode---hash碰撞(hash冲突)
     */

    @Override
    public boolean equals(Object o) {
        System.out.println("如果hash冲突,会调用该equals方法比较链表上是否存在相等对象");
        if (o == null || getClass() != o.getClass()) return false;
        Circle circle = (Circle) o;
        return Double.compare(radius, circle.radius) == 0;
    }

    @Override
    public int hashCode() {
        System.out.println("保存前都会调用该对象的hashcode方法确定hash值");
        return Double.hashCode(radius);
    }
}

LinkedHashSet

LinkedHashSet 继承自 HashSet,通过链表扩展功能,支持元素排序。

核心特性

  • 继承 HashSet 的所有特性,保证元素唯一。
  • 维护元素插入顺序,可按插入顺序遍历元素,解决 HashSet 无序问题。
  • 性能略低于 HashSet,但遍历效率更高(链表保证顺序)。

构造方法

  1. LinkedHashSet():构造空 LinkedHashSet。
  2. LinkedHashSet(Collection<? extends E> elements):构造 LinkedHashSet 并添加指定集合的所有元素。
  3. LinkedHashSet(int initialCapacity):构造指定容量的空 LinkedHashSet。
  4. LinkedHashSet(int initialCapacity, float loadFactor):构造指定容量和装填因子的空 LinkedHashSet。

示例代码

package com.set;

import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @author Jing61
 */
public class LinkedHashSetTest {
    public static void main(String[] args) {
        Set<String> set = new LinkedHashSet<>();
        set.add("peppa");
        set.add("emily");
        set.add("pedro");
        set.add("jorge");
        set.add("peppa");

        System.out.println(set);
        for (var e : set)
            System.out.println(e.toUpperCase());
    }
}

TreeSet

TreeSet 基于红黑树实现,能确保集合元素有序排列。

核心特性

  • 元素唯一且有序,排序方式分为自然排序和定制排序。
  • 无哈希冲突问题,查询、插入、删除操作时间复杂度为 O(log n)。
  • 需元素支持排序(实现 Comparable 接口)或创建实例时指定外部比较器(Comparator)。
  • 不允许插入空值。

构造方法

  1. TreeSet():构造空 TreeSet,默认按自然排序(元素需实现 Comparable 接口)。
  2. TreeSet(Collection<? extends E> elements):构造 TreeSet 并添加指定集合元素,按自然排序。
  3. TreeSet(Comparator<? super E> comparator):构造空 TreeSet,按指定比较器排序。
  4. TreeSet(SortedSet<E> s):构造 TreeSet,包含指定 SortedSet 的元素,沿用其排序规则。

示例代码

package com.set;

import java.util.Set;
import java.util.TreeSet;

public class TreeSetTest {
    public static void main(String[] args) {
        // 字符串自然排序(按字典序)
        Set<String> set = new TreeSet<>();
        set.add("peppa");
        set.add("emily");
        set.add("pedro");
        set.add("jorge");
        set.add("peppa"); // 重复元素,添加失败

        System.out.println(set); // 输出:[emily, jorge, peppa, pedro](字典序排序)

        /*
         * 定制排序:Circle 按半径降序排列
         * 1. 方式一:Circle 实现 Comparable 接口(自然排序)
         * 2. 方式二:创建实例时指定外部比较器(定制排序)
         */
        Set<Circle> circles = new TreeSet<>((Circle c1, Circle c2) -> 
            c1.getRadius() == c2.getRadius() ? 0 : (c1.getRadius() < c2.getRadius() ? 1 : -1)
        );
        circles.add(new Circle(4));
        circles.add(new Circle(2));
        circles.add(new Circle(3));
        circles.add(new Circle(3)); // 重复元素,添加失败
        
        System.out.println(circles); // 输出:[Circle{radius=4}, Circle{radius=3}, Circle{radius=2}](降序)
    }
}

复用上述 Circle 类(已实现 equals 和 hashCode,支持 Comparable 自然排序)

class Circle implements Comparable<Circle> {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    public double findArea() {
        return Math.PI * radius * radius;
    }

    @Override
    public String toString() {
        return "Circle[radius=" + radius + "]";
    }

    /**
     * 如果圆半径相等,圆对象相等
     * jdk规定:如果两个对象相等,那么它们的hashcode值一定相等
     * 两个不相等的对象可能存在相同的hashcode---hash碰撞(hash冲突)
     */

    @Override
    public boolean equals(Object o) {
        System.out.println("如果hash冲突,会调用该equals方法比较链表上是否存在相等对象");
        if (o == null || getClass() != o.getClass()) return false;
        Circle circle = (Circle) o;
        return Double.compare(radius, circle.radius) == 0;
    }

    @Override
    public int hashCode() {
        System.out.println("保存前都会调用该对象的hashcode方法确定hash值");
        return Double.hashCode(radius);
    }

    // 自然排序:按半径升序
    @Override
    public int compareTo(Circle o) {
        return Integer.compare(this.radius, o.radius);
    }
}

实现类选择建议

  1. 无需排序时,优先选 HashSet:效率最高,适合高频插入、删除和查找操作。
  2. 需保持插入顺序时,选 LinkedHashSet:兼顾唯一性和顺序性,遍历效率优。
  3. 需有序存储(自然排序/定制排序)时,选 TreeSet:无需额外排序操作,但元素需支持排序或指定比较器,性能略低于前两者。
posted @ 2025-11-12 17:45  Jing61  阅读(10)  评论(0)    收藏  举报