1. 简介
在本文中,我们将探讨如何在 Java 中将不同的集合类型进行合并。我们将从原生 Java 的方式入手,并结合一些流行的第三方库,如 Guava 和 Apache Commons,来展示各种实用的合并技巧。
如果你对 Java 集合还不太熟悉,可以先参考 Java 集合系列。
2. 使用的外部库
除了 Java 原生支持的集合操作外,我们还会用到一些第三方库。请将以下依赖添加到你的 pom.xml
文件中:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
最新版本可以在这里找到:
3. 合并数组(Array)
3.1. 使用 Java 原生的 arraycopy()
方法
Java 提供了一个原生方法 System.arraycopy()
,可以将一个数组的内容复制到另一个数组中:
Object[] combined = new Object[first.length + second.length];
System.arraycopy(first, 0, combined, 0, first.length);
System.arraycopy(second, 0, combined, first.length, second.length);
这个方法需要你手动指定源数组、起始位置、目标数组、目标起始位置以及复制的长度。✅ 优点是无依赖,性能好;❌ 缺点是代码略显啰嗦。
3.2. 使用 Java 8 的 Stream
API
Java 8 引入的 Stream
API 让集合操作变得简洁优雅:
Object[] combined = Stream.concat(Arrays.stream(first), Arrays.stream(second)).toArray();
Stream.concat()
会将两个流连接起来,再通过 toArray()
转为数组。这个方法也适用于其他集合类型,后面我们会看到。
3.3. 使用 Apache Commons 的 ArrayUtils.addAll()
Apache Commons 提供了更方便的方法:
Object[] combined = ArrayUtils.addAll(first, second);
这个方法简单粗暴,直接合并两个数组,适合不想手写复制逻辑的场景。
3.4. 使用 Guava 的 ObjectArrays.concat()
Guava 同样提供了合并数组的工具:
Object[] combined = ObjectArrays.concat(first, second, Object.class);
它支持泛型,需要传入目标类型的 Class 对象。
4. 合并 List
4.1. 使用 Collection.addAll()
原生方法
Java 的 Collection
接口提供了 addAll()
方法,可以将一个集合的所有元素添加到另一个集合中:
List<Object> combined = new ArrayList<>();
combined.addAll(first);
combined.addAll(second);
✅ 通用性强,适用于所有 Collection 子类。
4.2. 使用 Java 8 Stream API
可以使用 Stream.concat()
和 Collectors.toList()
来合并 List:
List<Object> combined = Stream.concat(first.stream(), second.stream()).collect(Collectors.toList());
或者使用 flatMap
:
List<Object> combined = Stream.of(first, second).flatMap(Collection::stream).collect(Collectors.toList());
这种方式更函数式,适合链式操作。
4.3. 使用 Apache Commons 的 ListUtils.union()
List<Object> combined = ListUtils.union(first, second);
⚠️ 注意:union()
并不是简单的合并,而是做并集操作,可能会去重。
4.4. 使用 Guava 的 Iterables.concat()
Iterable<Object> combinedIterables = Iterables.unmodifiableIterable(Iterables.concat(first, second));
List<Object> combined = Lists.newArrayList(combinedIterables);
Guava 提供的 concat()
方法适用于多个 Iterable,合并后可以轻松转为 List。
5. 合并 Set
5.1. 使用 Java 原生方式
和 List 类似,Set 也可以使用 addAll()
方法:
Set<Object> combined = new HashSet<>();
combined.addAll(first);
combined.addAll(second);
5.2. 使用 Java 8 Stream API
Set<Object> combined = Stream.concat(first.stream(), second.stream()).collect(Collectors.toSet());
⚠️ 注意:使用 Collectors.toSet()
可能会去重,视 Set 实现而定。
也可以使用 flatMap
:
Set<Object> combined = Stream.of(first, second).flatMap(Collection::stream).collect(Collectors.toSet());
5.3. 使用 Apache Commons 的 SetUtils.union()
Set<Object> combined = SetUtils.union(first, second);
✅ 返回的是两个 Set 的并集。
5.4. 使用 Guava 的 Sets.union()
Set<Object> combined = Sets.union(first, second);
Guava 的 union()
方法返回的是一个视图,延迟计算,性能更优。
6. 合并 Map
6.1. 使用 Java 原生方式
Map 提供了 putAll()
方法,可以将另一个 Map 的键值对全部复制过来:
Map<Object, Object> combined = new HashMap<>();
combined.putAll(first);
combined.putAll(second);
✅ 简单直观,适合大多数场景。
6.2. 使用 Java 8 的 merge()
方法
Java 8 引入了 merge()
方法,可以处理键冲突的情况:
second.forEach((key, value) -> first.merge(key, value, String::concat));
第三个参数是合并函数,用于处理键冲突时的值合并逻辑。
也可以使用 flatMap
:
Map<String, String> combined = Stream.of(first, second)
.map(Map::entrySet)
.flatMap(Collection::stream)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, String::concat));
6.3. 使用 Apache Commons Exec 的 MapUtils.merge()
Map<String, String> combined = MapUtils.merge(first, second);
✅ 适用于简单的 Map 合并,不处理键冲突。
6.4. 使用 Guava 的 ImmutableMap.Builder
Map<String, String> combined = ImmutableMap.<String, String>builder()
.putAll(first)
.putAll(second)
.build();
Guava 的 ImmutableMap
是不可变集合,适合构建后不修改的场景。
7. 总结
本文介绍了在 Java 中合并不同集合类型(数组、List、Set、Map)的多种方式,涵盖了原生 Java 和第三方库的解决方案。在实际开发中,可以根据性能、可读性以及是否需要处理重复元素等需求来选择合适的方法。
所有示例代码及测试用例可以在 GitHub 上找到。