1. 引言

本文将演示如何将两个集合压缩(zip)成一个逻辑集合。注意:zip 操作与常见的 concat 或 merge 有本质区别。后者只是简单地将新集合附加到现有集合末尾,而 zip 操作会从每个集合中取出对应元素进行组合。

Java 核心库并未内置 zip 支持,但第三方库提供了这个实用功能。考虑两个列表:一个存储人名,另一个存储年龄:

List<String> names = new ArrayList<>(Arrays.asList("John", "Jane", "Jack", "Dennis"));

List<Integer> ages = new ArrayList<>(Arrays.asList(24, 25, 27));

压缩后,我们将得到由两个集合对应元素组成的 name-age 对。

2. 使用 Java 8 的 IntStream

通过核心 Java,我们可以用 IntStream 生成索引,再提取两个集合的对应元素:

IntStream
  .range(0, Math.min(names.size(), ages.size()))
  .mapToObj(i -> names.get(i) + ":" + ages.get(i))
  // ...

⚠️ 注意:这里用 Math.min 处理长度不一致的情况,避免越界异常。

3. 使用 Guava Streams

从版本 21 开始,Google Guava 在 Streams 类中提供了 zip 辅助方法。这消除了手动创建和映射索引的麻烦,语法更简洁:

Streams
  .zip(names.stream(), ages.stream(), (name, age) -> name + ":" + age)
  // ...

✅ 优势:代码更直观,直接表达"将两个流按位置合并"的意图。

4. 使用 jOOλ (jOOL)

jOOL 在 Java 8 Lambda 基础上提供了许多增强功能。得益于 Tuple1Tuple16 的支持,zip 操作变得更灵活:

Seq
  .of("John","Jane", "Dennis")
  .zip(Seq.of(24,25,27));

这将生成包含压缩元组的 Seq 结果:

(tuple(1, "a"), tuple(2, "b"), tuple(3, "c"))

jOOL 的 zip 方法还支持自定义转换函数:

Seq
  .of(1, 2, 3)
  .zip(Seq.of("a", "b", "c"), (x, y) -> x + ":" + y);

如果只需与索引压缩,可用 zipWithIndex 方法:

Seq.of("a", "b", "c").zipWithIndex();

4.1. 各方案对比

方案 优势 劣势
Java 8 IntStream 无需第三方库 代码稍显啰嗦
Guava Streams API 简洁,符合流式编程风格 依赖 Guava 库
jOOλ 支持元组,功能最丰富 学习曲线稍陡

5. 总结

本文探讨了在 Java 中实现集合 zip 操作的三种方式:

  • ✅ 核心库方案(IntStream)适合无依赖场景
  • ✅ Guava 提供最简洁的流式 API
  • ✅ jOOL 适合需要元组或复杂转换的场景

踩坑提醒:处理不等长集合时,务必明确截断策略(如 Math.min),否则可能抛出 IndexOutOfBoundsException

完整代码示例可在 GitHub 获取。


原始标题:Zipping Collections in Java