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+工厂方法
- 避免手动循环实现(代码冗余易错)
掌握这些转换技巧,能显著提升集合操作的灵活性和代码质量。