Java 实例 – 数组并集(千字长文)

Java 实例 – 数组并集:从零开始掌握集合运算

在日常开发中,我们经常需要处理多个数据集合之间的关系。比如,两个用户列表合并去重、两个商品分类合并展示、两个日志时间段合并分析等。这些场景背后,其实都涉及“并集”这个基础数学概念。今天我们就来深入聊聊 Java 实例 – 数组并集 的实现方式,帮助你从基础到进阶,掌握这个实用技能。

并集,简单来说就是两个集合中所有不重复元素的合并结果。比如集合 A = {1, 2, 3},集合 B = {3, 4, 5},那么它们的并集就是 {1, 2, 3, 4, 5}。注意,重复元素只保留一份。

在 Java 中,数组是基础数据结构,但不像集合类那样自带并集操作。因此我们需要手动实现。接下来,我们就一步步来。


什么是数组并集?通俗理解

想象你有两个朋友,分别整理了他们喜欢的电影列表:

  • 小明:《肖申克的救赎》《盗梦空间》《阿甘正传》
  • 小红:《盗梦空间》《星际穿越》《阿甘正传》《泰坦尼克号》

现在你想知道他们俩都喜欢哪些电影,不管谁先列的,只要出现过就算。这就是“并集”——把两个列表合并,去重后展示。

在 Java 中,我们用数组来表示这些电影名字,然后通过代码实现“合并去重”。这正是我们要掌握的核心技能。


创建数组与初始化

首先,我们得有数据。Java 中数组是固定长度的容器,适合存储同类型数据。我们用字符串数组来表示电影名。

// 定义小明喜欢的电影列表
String[] moviesXiaoMing = {"肖申克的救赎", "盗梦空间", "阿甘正传"};

// 定义小红喜欢的电影列表
String[] moviesXiaoHong = {"盗梦空间", "星际穿越", "阿甘正传", "泰坦尼克号"};

这里要注意,数组的长度在创建后不能改变。所以如果未来想动态添加元素,建议使用 ArrayList,但我们今天先聚焦数组,因为这是“Java 实例 – 数组并集”的核心前提。


实现并集的三种主流方法

方法一:使用 Set 去重(推荐)

Set 是 Java 集合框架中一个非常重要的接口,它的核心特性就是“自动去重”。我们可以通过将数组转换为 Set,再合并,最后转回数组。

import java.util.*;

public class ArrayUnion {
    public static void main(String[] args) {
        // 原始数据
        String[] moviesXiaoMing = {"肖申克的救赎", "盗梦空间", "阿甘正传"};
        String[] moviesXiaoHong = {"盗梦空间", "星际穿越", "阿甘正传", "泰坦尼克号"};

        // 第一步:将两个数组转换为 Set,自动去重
        Set<String> set1 = new HashSet<>(Arrays.asList(moviesXiaoMing));
        Set<String> set2 = new HashSet<>(Arrays.asList(moviesXiaoHong));

        // 第二步:合并两个 Set,使用 addAll 方法
        set1.addAll(set2);

        // 第三步:将合并后的 Set 转换为数组
        String[] unionMovies = set1.toArray(new String[0]);

        // 输出结果
        System.out.println("合并后的电影列表(并集):");
        for (String movie : unionMovies) {
            System.out.println(movie);
        }
    }
}

代码说明:

  • Arrays.asList():将数组转为 List,便于后续操作。
  • new HashSet<>(...):创建一个 HashSet,自动去重。
  • addAll():将一个集合的所有元素添加到另一个集合中。
  • toArray(new String[0]):将 Set 转为字符串数组,参数 new String[0] 是类型提示,避免泛型擦除问题。

✅ 这种方法最简洁高效,适合大多数场景,是 Java 实例 – 数组并集 的首选方案。


方法二:手动遍历 + 判断去重

如果你不想用集合类,也可以手动实现。这有助于理解并集的本质。

public class ArrayUnionManual {
    public static void main(String[] args) {
        String[] moviesXiaoMing = {"肖申克的救赎", "盗梦空间", "阿甘正传"};
        String[] moviesXiaoHong = {"盗梦空间", "星际穿越", "阿甘正传", "泰坦尼克号"};

        // 创建结果数组,初始长度为两个数组之和
        List<String> result = new ArrayList<>();

        // 遍历第一个数组
        for (String movie : moviesXiaoMing) {
            result.add(movie);  // 直接添加,因为是第一个
        }

        // 遍历第二个数组,判断是否已存在
        for (String movie : moviesXiaoHong) {
            boolean exists = false;
            for (String item : result) {
                if (item.equals(movie)) {
                    exists = true;
                    break;  // 找到就跳出内层循环
                }
            }
            if (!exists) {
                result.add(movie);  // 不存在才添加
            }
        }

        // 转为数组输出
        String[] unionMovies = result.toArray(new String[0]);
        System.out.println("手动实现的并集结果:");
        for (String movie : unionMovies) {
            System.out.println(movie);
        }
    }
}

代码说明:

  • 使用 ArrayList 作为临时容器,因为它可以动态扩容。
  • 外层循环处理第一个数组,直接加入。
  • 内层循环检查第二个数组中的每个元素是否已在结果中。
  • equals() 方法用于字符串比较,比 == 更安全。
  • 时间复杂度为 O(n×m),适合小数据量。

⚠️ 优点是逻辑清晰,适合学习;缺点是性能较差,不推荐在大数据量时使用。


方法三:使用 Java 8 Stream API(现代写法)

Java 8 引入了 Stream API,让集合操作变得非常简洁。我们也可以用它来实现并集。

import java.util.*;
import java.util.stream.Collectors;

public class ArrayUnionStream {
    public static void main(String[] args) {
        String[] moviesXiaoMing = {"肖申克的救赎", "盗梦空间", "阿甘正传"};
        String[] moviesXiaoHong = {"盗梦空间", "星际穿越", "阿甘正传", "泰坦尼克号"};

        // 使用 Stream 合并并去重
        List<String> unionMovies = Arrays.stream(moviesXiaoMing)
                .distinct()  // 去重(可选,防止第一个数组重复)
                .collect(Collectors.toList())
                .stream()
                .distinct()  // 再次去重
                .collect(Collectors.toList());

        // 将第二个数组合并进来
        unionMovies.addAll(
            Arrays.stream(moviesXiaoHong)
                  .distinct()
                  .collect(Collectors.toList())
        );

        // 最终去重并转为数组
        String[] result = unionMovies.stream()
                .distinct()
                .toArray(String[]::new);

        System.out.println("Stream API 实现的并集:");
        Arrays.stream(result).forEach(System.out::println);
    }
}

代码说明:

  • Arrays.stream():将数组转为 Stream。
  • distinct():去除重复元素。
  • Collectors.toList():将 Stream 转为 List。
  • toArray(String[]::new):将 List 转为数组,使用构造器引用。

✅ 这种写法现代、优雅,适合在项目中使用。虽然代码稍长,但可读性高。


性能对比与选择建议

方法 时间复杂度 空间复杂度 优点 缺点
Set 去重 O(n + m) O(n + m) 快速、简洁、推荐 需要额外集合类
手动遍历 O(n×m) O(n + m) 逻辑清晰,无依赖 性能差,不适合大数据
Stream API O(n + m) O(n + m) 代码优雅,适合函数式编程 学习成本略高

建议:

  • 日常开发中,优先使用 Set 去重法,它是 Java 实例 – 数组并集 的最佳实践。
  • 学习阶段可以尝试手动实现,加深理解。
  • 项目中若已使用 Stream,可考虑 Stream 方案。

实际应用场景举例

  1. 用户权限合并:两个角色的权限列表合并,去重后生成最终权限。
  2. 日志时间范围合并:多个时间段可能有重叠,合并后形成连续区间。
  3. 商品分类合并:两个分类下的商品 ID 合并,去重后展示。
  4. 数据清洗:从多个来源获取数据,合并去重,避免重复处理。

这些场景都离不开“并集”运算。掌握 Java 实例 – 数组并集,意味着你具备了处理真实业务数据的基本能力。


常见误区与注意事项

  • ❌ 不要用 == 比较字符串:应使用 equals()
  • ❌ 不要直接用 set.toArray() 无参:可能导致类型错误,应传入 new String[0]
  • ❌ 不要忽略数组长度:如果数组很大,手动遍历可能超时。
  • ✅ 始终考虑去重逻辑:并集的核心就是“不重复”。

总结

今天我们深入探讨了 Java 实例 – 数组并集 的多种实现方式。从最基础的数组定义,到 Set 去重、手动遍历、Stream API,每一种方法都有其适用场景。

  • 对初学者:推荐从手动实现入手,理解并集的原理。
  • 对中级开发者:应掌握 Set 方案,它是高效、稳定的首选。
  • 对高级开发者:可结合 Stream API,写出更现代、可读性更强的代码。

记住,编程不是记住代码,而是理解逻辑。当你能用不同方式解决同一个问题时,你才算真正掌握了它。

希望今天的分享能帮你打通“数组并集”这道关卡。下次遇到数据合并问题,别再手忙脚乱,拿出你的工具箱,优雅地解决问题。