1. 概述

本文将深入探讨 List与Set之间的转换,涵盖原生Java实现、Guava库、Apache Commons Collections以及Java 10+的新特性。作为“Java基础回顾”系列的一部分,本文专为有经验的开发者设计,跳过基础概念,直击核心实现。

2. 将List转换为Set

2.1. 原生Java实现:使用构造函数

最直接的方式是利用Set的构造函数:

public void givenUsingCoreJava_whenListConvertedToSet_thenCorrect() {
    List<Integer> sourceList = Arrays.asList(0, 1, 2, 3, 4, 5);
    Set<Integer> targetSet = new HashSet<>(sourceList);
}

关键点:所有集合构造函数都接受其他集合作为源,转换过程类型安全且高效。

2.2. 原生Java实现:使用循环

手动遍历添加元素:

public void givenUsingCoreJava_whenUsingLoopConvertToSet_thenCorrect() {
    List<Integer> sourceList = Lists.newArrayList(0, 1, 2, 3, 4, 5);
    Set<Integer> targetSet = new HashSet<>();
    for (Integer element : sourceList) {
        targetSet.add(element);
    }
}

⚠️ 注意:虽然可行,但代码冗余,不如构造函数简洁。

2.3. 原生Java实现:使用addAll()方法

利用Collection接口的通用方法:

public final void givenUsingCoreJava_whenUsingAddAllToSet_thenCorrect() {
    List<Integer> sourceList = Lists.newArrayList(0, 1, 2, 3, 4, 5);
    Set<Integer> targetSet = new HashSet<>();
    targetSet.addAll(sourceList);
}

优势addAll()是Collection接口方法,适用于所有集合类型间的转换。

2.4. 原生Java实现:使用Stream API

Java 8+的函数式方案:

public void givenUsingCoreJava_whenUsingStreamToSet_thenCorrect() {
    List<Integer> sourceList = Lists.newArrayList(0, 1, 2, 3, 4, 5);
    Set<Integer> targetSet = sourceList.stream().collect(Collectors.toSet());
}

特点:一行代码搞定,底层自动处理去重逻辑。

2.5. 使用Guava库

Google Guava的简化方案:

public void givenUsingGuava_whenListConvertedToSet_thenCorrect() {
    List<Integer> sourceList = Lists.newArrayList(0, 1, 2, 3, 4, 5);
    Set<Integer> targetSet = Sets.newHashSet(sourceList);
}

优势:代码更简洁,但需引入Guava依赖。

2.6. 使用Apache Commons Collections

Apache Commons的解决方案:

public void givenUsingCommonsCollections_whenListConvertedToSet_thenCorrect() {
    List<Integer> sourceList = Lists.newArrayList(0, 1, 2, 3, 4, 5);
    Set<Integer> targetSet = new HashSet<>(6);
    CollectionUtils.addAll(targetSet, sourceList);
}

⚠️ 注意:需手动初始化Set容量,避免扩容开销。

2.7. 使用Java 10+特性

Java 10引入的不可变集合工厂:

public void givenUsingJava10_whenListConvertedToSet_thenCorrect() {
    List sourceList = Lists.newArrayList(0, 1, 2, 3, 4, 5);
    Set targetSet = Set.copyOf(sourceList);
}

⚠️ 重要:生成的Set是不可变集合,尝试修改会抛出UnsupportedOperationException

3. 将Set转换为List

3.1. 原生Java实现:使用构造函数

利用ArrayList构造函数:

public void givenUsingCoreJava_whenSetConvertedToList_thenCorrect() {
   Set<Integer> sourceSet = Sets.newHashSet(0, 1, 2, 3, 4, 5);
   List<Integer> targetList = new ArrayList<>(sourceSet);
}

关键点:与List转Set完全对称的实现。

3.2. 原生Java实现:使用循环

手动遍历添加:

public void givenUsingCoreJava_whenUsingLoop_thenCorrect() {
    Set<Integer> sourceSet = Sets.newHashSet(0, 1, 2, 3, 4, 5);
    List<Integer> targetList = new ArrayList<>();
    for (Integer element : sourceSet) {
        targetList.add(element);
    }
}

缺点:冗余代码,实际开发中应避免。

3.3. 原生Java实现:使用addAll()方法

复用Collection接口方法:

public void givenUsingCoreJava_whenUsingAddAll_thenCorrect() {
    Set<Integer> sourceSet = Sets.newHashSet(0, 1, 2, 3, 4, 5);
    List<Integer> targetList = new ArrayList<>();
    targetList.addAll(sourceSet);
}

优势:与List转Set逻辑一致,代码复用性强。

3.4. 原生Java实现:使用Stream API

Stream的toList收集器:

public void givenUsingCoreJava_whenUsingStream_thenCorrect() {
    Set<Integer> sourceSet = Sets.newHashSet(0, 1, 2, 3, 4, 5);
    List<Integer> targetList = sourceSet.stream().collect(Collectors.toList());
}

特点:函数式风格,代码简洁。

3.5. 使用Guava库

Guava的工厂方法:

public void givenUsingGuava_whenSetConvertedToList_thenCorrect() {
    Set<Integer> sourceSet = Sets.newHashSet(0, 1, 2, 3, 4, 5);
    List<Integer> targetList = Lists.newArrayList(sourceSet);
}

优势:代码量最少,语义清晰。

3.6. 使用Apache Commons Collections

Commons Collections的通用方案:

public void givenUsingCommonsCollections_whenSetConvertedToList_thenCorrect() {
    Set<Integer> sourceSet = Sets.newHashSet(0, 1, 2, 3, 4, 5);
    List<Integer> targetList = new ArrayList<>(6);
    CollectionUtils.addAll(targetList, sourceSet);
}

⚠️ 注意:同样需要预设容量以优化性能。

3.7. 使用Java 10+特性

不可变List工厂方法:

public void givenUsingJava10_whenSetConvertedToList_thenCorrect() {
    Set<Integer> sourceSet = Sets.newHashSet(0, 1, 2, 3, 4, 5);
    List<Integer> targetList = List.copyOf(sourceSet);
}

⚠️ 重要:生成的List是不可变集合,适用于只读场景。

4. List与TreeSet的转换

TreeSet要求元素实现Comparable接口,转换时需特别注意排序规则。以Employee类为例:

public class Employee implements Comparable<Employee> {
    private int employeeId;
    private String employeeName;

    Employee(int employeeId, String employeeName) {
        this.employeeId = employeeId;
        this.employeeName = employeeName;
    }

    int getEmployeeId() {
        return employeeId;
    }

    public String getEmployeeName() {
        return employeeName;
    }

    @Override
    public String toString() {
        return employeeId + " " + employeeName;
    }

    @Override
    public int compareTo(Employee o) {
        if (this.employeeId == o.employeeId) {
            return 0;
        } else if (this.employeeId < o.employeeId) {
            return 1;
        } else {
            return -1;
        }
    }
}

4.1. 将List转换为TreeSet

利用TreeSet构造函数自动排序:

@Test
public void givenComparableObject_whenConvertingToTreeSet_thenNoExceptionThrown() {
    ArrayList<Employee> arrayList = new ArrayList<>();
    arrayList.add(new Employee(3, "John"));
    arrayList.add(new Employee(5, "Mike"));
    arrayList.add(new Employee(2, "Bob"));
    arrayList.add(new Employee(1, "Tom"));
    arrayList.add(new Employee(4, "Johnny"));
        
    assertDoesNotThrow(()->{
        TreeSet<Employee> treeSet = new TreeSet<>(arrayList);
    });
}

关键点:TreeSet会根据compareTo()规则自动排序元素。

4.2. 将TreeSet转换为List

使用ArrayList构造函数:

@Test
public void givenTreeSet_whenConvertingToList_thenNoExceptionThrown() {
    TreeSet<Employee> treeSet = new TreeSet<>();
    treeSet.add(new Employee(3, "John"));
    treeSet.add(new Employee(5, "Mike"));
    treeSet.add(new Employee(2, "Bob"));
    treeSet.add(new Employee(1, "Tom"));
    treeSet.add(new Employee(4, "Johnny"));
        
    assertDoesNotThrow(()->{
        ArrayList<Employee> arrayList = new ArrayList<>(treeSet);
    });
}

结果:List元素顺序与TreeSet的排序规则一致。

5. 总结

本文系统梳理了Java中List与Set转换的多种实现方式:

场景 推荐方案 注意事项
List → Set new HashSet<>(list) 原生Java,无依赖
Set → List new ArrayList<>(set) 原生Java,无依赖
需要不可变集合 Set.copyOf() / List.copyOf() Java 10+,只读场景
TreeSet转换 构造函数 + Comparable实现 必须定义排序规则

最佳实践建议

  • 优先使用原生Java构造函数(简洁高效)
  • 复杂场景考虑Stream API(函数式风格)
  • 不可变集合需求用Java 10+工厂方法
  • 避免手动循环实现(代码冗余易错)

掌握这些转换技巧,能显著提升集合操作的灵活性和代码质量。


原始标题:Converting between a List and a Set in Java | Baeldung