本文目录导读:

在Java中比对集合差异是日常开发中的常见需求,根据你的具体场景(如判断是否相等、找出差集、交集、并集,还是实现自定义的比对逻辑),有不同的实现方法。
下面我为你总结了 4种最常用 的场景和对应的核心代码方案。
判断两个集合是否“完全相同”
这是最基础的需求,注意区分 equals 和 containsAll。
使用 equals 方法(要求顺序也一致)
ArrayList 的 equals 方法会比较元素以及元素顺序。
List<String> list1 = Arrays.asList("A", "B", "C");
List<String> list2 = Arrays.asList("A", "B", "C");
List<String> list3 = Arrays.asList("C", "B", "A");
System.out.println(list1.equals(list2)); // true (顺序一致)
System.out.println(list1.equals(list3)); // false (顺序不一致)
忽略顺序,只判断元素是否相同
先将 List 转为 Set,或者对 List 进行排序。
// 方法1:转为 Set Set<String> set1 = new HashSet<>(list1); Set<String> set2 = new HashSet<>(list3); System.out.println(set1.equals(set2)); // true // 方法2:排序后比较 Collections.sort(list1); Collections.sort(list3); System.out.println(list1.equals(list3)); // true
计算集合的差集、交集、并集
这是最核心的案例需求,推荐使用 Collection 接口原生提供的 retainAll(交集)、removeAll(差集)、addAll(并集)。
注意:这些方法会直接修改原集合,如果你希望保留原数据,请先
new一个新集合。
List<Integer> listA = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> listB = new ArrayList<>(Arrays.asList(4, 5, 6, 7, 8));
// 1. 交集 (A ∩ B) - 在 A 中且同时在 B 中的元素
List<Integer> intersect = new ArrayList<>(listA);
intersect.retainAll(listB);
System.out.println("交集: " + intersect); // [4, 5]
// 2. 差集 (A - B) - 在 A 中但不在 B 中的元素
List<Integer> diffA = new ArrayList<>(listA);
diffA.removeAll(listB);
System.out.println("A - B 差集: " + diffA); // [1, 2, 3]
// 3. 并集 (A ∪ B) - 所有元素合并,去重
// 方法A:使用 HashSet (无序)
Set<Integer> unionSet = new HashSet<>(listA);
unionSet.addAll(listB);
System.out.println("并集 (Set): " + unionSet); // [1, 2, 3, 4, 5, 6, 7, 8]
// 方法B:使用 ArrayList (保留顺序,但不去重)
List<Integer> unionList = new ArrayList<>(listA);
unionList.addAll(listB);
System.out.println("并集 (List): " + unionList); // [1, 2, 3, 4, 5, 4, 5, 6, 7, 8]
// 4. 对称差集 (A ∪ B) - (A ∩ B) - 只在一个集合中存在的元素
List<Integer> symmetricDiff = new ArrayList<>(listA);
symmetricDiff.addAll(listB); // 首先得到并集
List<Integer> tmp = new ArrayList<>(listA);
tmp.retainAll(listB); // 然后得到交集
symmetricDiff.removeAll(tmp); // 最后从并集中移除交集
System.out.println("对称差集: " + symmetricDiff); // [1, 2, 3, 6, 7, 8]
比对自定义对象(实体类)的差异
这是最复杂也最现实的场景,例如比对两个 List<User> 中哪些用户是新增、删除、修改的。
核心思路:需要重写 equals 和 hashCode 方法(通常根据业务主键,如用户ID)。
// 假设 User 类已经重写了 equals 和 hashCode (基于 id)
public class User {
private Integer id;
private String name;
// ... getters/setters, 以及基于 id 的 equals/hashCode
}
// 比对逻辑
List<User> oldList = getOldUsers(); // 数据库里的旧数据
List<User> newList = getNewUsers(); // 输入的新数据
// 1. 找出需要删除的用户 (在旧集合,不在新集合)
List<User> toDelete = new ArrayList<>(oldList);
toDelete.removeAll(newList); // 注意:这里依赖 User 的 equals 方法比较的是 id
// 2. 找出需要新增的用户 (在新集合,不在旧集合)
List<User> toAdd = new ArrayList<>(newList);
toAdd.removeAll(oldList);
// 3. 找出需要更新的用户 (在新集合中,并且在旧集合中也存在)
// 通常通过遍历新集合,检查旧集合是否包含,然后判断字段是否变化
List<User> toUpdate = new ArrayList<>();
for (User newUser : newList) {
// 如果旧集合中存在同一个 ID 的用户
if (oldList.contains(newUser)) {
// 进一步比对具体字段(name)
// 通过 indexOf 找到旧集合中的对象
User oldUser = oldList.get(oldList.indexOf(newUser));
if (!oldUser.getName().equals(newUser.getName())) {
toUpdate.add(newUser);
}
}
}
关键:removeAll 和 contains 都依赖于你重写的 equals 方法。
使用 Stream API(Java 8+)
如果你更喜欢函数式编程风格,或者不想修改集合本身(不可变集合),用 Stream 非常强大且优雅。
List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> list2 = Arrays.asList(4, 5, 6, 7, 8);
// 1. 交集
List<Integer> intersect = list1.stream()
.filter(list2::contains)
.collect(Collectors.toList());
System.out.println("交集: " + intersect); // [4, 5]
// 2. 差集 (A - B)
List<Integer> diffA = list1.stream()
.filter(e -> !list2.contains(e))
.collect(Collectors.toList());
System.out.println("差集: " + diffA); // [1, 2, 3]
// 3. 并集 (去重)
List<Integer> union = Stream.concat(list1.stream(), list2.stream())
.distinct()
.collect(Collectors.toList());
System.out.println("并集: " + union); // [1, 2, 3, 4, 5, 6, 7, 8]
// 4. 对象比对:找出 List<User> 中新增或删除的
// (假设 User 有 equals & hashCode)
List<User> oldUsers = ...;
List<User> newUsers = ...;
List<User> added = newUsers.stream()
.filter(u -> !oldUsers.contains(u))
.collect(Collectors.toList());
List<User> removed = oldUsers.stream()
.filter(u -> !newUsers.contains(u))
.collect(Collectors.toList());
总结与建议
| 需求场景 | 推荐方法 | 注意事项 |
|---|---|---|
| 简单判断是否相等 | list1.equals(list2) (顺序敏感) / Set.equals (顺序不敏感) |
基础类型/字符串直接可用;自定义对象需重写 equals。 |
| 计算差集、交集、并集 | retainAll()、removeAll()、addAll() |
会修改原集合,记得先 new 拷贝。 |
| 比对自定义对象复杂差异 | removeAll + 遍历 + indexOf |
必须重写 equals/hashCode,否则比较的是内存地址。 |
| 函数式/不可变集合 | Stream API (filter + collect) |
代码简洁,适合现代 Java 开发。 |
| 高性能大集合 (10万+) | 转为 HashSet 再使用 contains |
List.contains 是 O(n),Set.contains 是 O(1)。 |
一句话总结:普通比对用 retainAll/removeAll,对象比对重写 equals,大数据量先用 HashSet。