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 上找到。


原始标题:Combining Different Types of Collections in Java | Baeldung