Java 中将 List 中对象的某一列转换为 Set

在 Java 中将 List 中对象的某一列转换为 Set,有几种常用方法:

1. 使用 Stream API(最常用)

复制代码
import java.util.*;
import java.util.stream.Collectors;

// 示例类
class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() { return name; }
    public int getAge() { return age; }
}

public class Main {
    public static void main(String[] args) {
        List<Person> personList = Arrays.asList(
            new Person("张三", 25),
            new Person("李四", 30),
            new Person("王五", 25),
            new Person("张三", 28)  // 重复的张三
        );
        
        // 方法1:提取 name 列到 Set(自动去重)
        Set<String> nameSet = personList.stream()
            .map(Person::getName)  // 提取 name
            .collect(Collectors.toSet());
        
        System.out.println(nameSet);  // [张三, 李四, 王五]
        
        // 方法2:提取 age 列到 Set
        Set<Integer> ageSet = personList.stream()
            .map(Person::getAge)
            .collect(Collectors.toSet());
        
        System.out.println(ageSet);  // [25, 30, 28]
    }
}

2. 指定具体的 Set 实现

复制代码
// 使用 HashSet
Set<String> nameSet = personList.stream()
    .map(Person::getName)
    .collect(Collectors.toCollection(HashSet::new));

// 使用 TreeSet(排序)
Set<String> sortedNameSet = personList.stream()
    .map(Person::getName)
    .collect(Collectors.toCollection(TreeSet::new));

// 使用 LinkedHashSet(保持插入顺序)
Set<String> linkedNameSet = personList.stream()
    .map(Person::getName)
    .collect(Collectors.toCollection(LinkedHashSet::new));

3. 处理可能为 null 的情况

复制代码
// 方法1:过滤掉 null
Set<String> nameSet = personList.stream()
    .map(Person::getName)
    .filter(Objects::nonNull)  // 过滤 null
    .collect(Collectors.toSet());

// 方法2:使用 filter 和 Optional
Set<String> nameSet = personList.stream()
    .map(Person::getName)
    .filter(name -> name != null && !name.trim().isEmpty())  // 过滤 null 和空字符串
    .collect(Collectors.toSet());

4. 复杂对象属性提取

复制代码
// 如果属性是嵌套对象
class Department {
    private String deptName;
    // getters and setters
}

class Employee {
    private String name;
    private Department department;
    // getters and setters
}

// 提取嵌套属性
Set<String> deptNames = employeeList.stream()
    .map(Employee::getDepartment)
    .filter(Objects::nonNull)
    .map(Department::getDeptName)
    .collect(Collectors.toSet());

5. 并行流处理(大数据量时)

复制代码
Set<String> nameSet = personList.parallelStream()  // 并行处理
    .map(Person::getName)
    .collect(Collectors.toSet());

6. 传统方法(不使用 Stream)

复制代码
// 传统 for 循环
Set<String> nameSet = new HashSet<>();
for (Person person : personList) {
    nameSet.add(person.getName());
}

// 传统 for 循环,处理 null
Set<String> nameSet = new HashSet<>();
for (Person person : personList) {
    if (person.getName() != null) {
        nameSet.add(person.getName());
    }
}

主要区别对比

方法 优点 缺点
Stream API 代码简洁,可读性好,支持链式调用 Java 8+
并行流 大数据量性能好 线程安全需注意
传统循环 兼容性好,Java 8 以下可用 代码冗长

最佳实践建议

  1. 推荐使用 Stream API:代码简洁,可读性好

  2. 考虑使用 LinkedHashSet:如果需要保持顺序

  3. 总是处理 null 值:避免 NullPointerException

  4. 大数据量考虑并行流:但要注意线程安全问题

  5. 使用具体类型:明确指定 Set 的实现类型,便于维护

相关推荐
程序员老陆4 分钟前
嵌入式 Qt:QWidgets 还是 QML / Qt Quick?
开发语言·qt
.道阻且长.6 分钟前
C++11 :新的类功能,lambda,包装器
开发语言·c++·后端
言乐67 分钟前
JavaScript概括前端原理
开发语言·前端·javascript·python·ecmascript
liulilittle7 分钟前
短期内不存在通用 AI 工作流魔法
大数据·开发语言·c++·人工智能·llm·agent·tools
郝学胜-神的一滴23 分钟前
C++ Templates 03:手写一个Stack看透模板核心机制
开发语言·c++·产品运营·软件工程·产品经理
(Charon)38 分钟前
【C/C++面试】手写内存池:从空闲链表到内存申请与释放
java·开发语言·windows
EatFan39 分钟前
Spring Boot 4 迁移避坑清单:Jackson 3、starter 拆分与最低 JDK 口径核对(含若依/芋道/CRMEB 升级对照)
java·数据库·spring boot·spring boot 4·java 21·jakarta ee 11·jackson 3
栖凤40 分钟前
多 Agent 工作流实践:从单打独斗到协同作战
java·linux·服务器
派小心.40 分钟前
页面关闭前埋点丢失:sendBeacon验收
开发语言·php
一木 之林41 分钟前
DeepSeek Agent 开发
java·前端·人工智能