Java8 Stream 实现复杂集合对象的差集、并集、交集

JAVA herman 813浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:codedq,发送下载链接帮助你免费下载!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:codedq,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

Java8 Stream 实现复杂集合对象的差集、并集、交集!

昨天群里一位网友想要获取两个 List 集合之间的差集、并集、交集!群里网友群策群力,很多有人给他百度搜索到了一个结果。demo 案例如下所示:

import java.util.ArrayList;
import java.util.List;
import static java.util.stream.Collectors.toList;
 
public class Test {
 
    public static void main(String[] args) {
        List<String> list1 = new ArrayList<String>();
        list1.add("1");
        list1.add("2");
        list1.add("3");
        list1.add("5");
        list1.add("6");
 
        List<String> list2 = new ArrayList<String>();
        list2.add("2");
        list2.add("3");
        list2.add("7");
        list2.add("8");
 
        // 交集
        List<String> intersection = list1.stream().filter(item -> list2.contains(item)).collect(toList());
        System.out.println("---交集 intersection---");
        intersection.parallelStream().forEach(System.out :: println);
 
        // 差集 (list1 - list2)
        List<String> reduce1 = list1.stream().filter(item -> !list2.contains(item)).collect(toList());
        System.out.println("---差集 reduce1 (list1 - list2)---");
        reduce1.parallelStream().forEach(System.out :: println);
 
        // 差集 (list2 - list1)
        List<String> reduce2 = list2.stream().filter(item -> !list1.contains(item)).collect(toList());
        System.out.println("---差集 reduce2 (list2 - list1)---");
        reduce2.parallelStream().forEach(System.out :: println);
 
        // 并集
        List<String> listAll = list1.parallelStream().collect(toList());
        List<String> listAll2 = list2.parallelStream().collect(toList());
        listAll.addAll(listAll2);
        System.out.println("---并集 listAll---");
        listAll.parallelStream().forEachOrdered(System.out :: println);
 
        // 去重并集
        List<String> listAllDistinct = listAll.stream().distinct().collect(toList());
        System.out.println("---得到去重并集 listAllDistinct---");
        listAllDistinct.parallelStream().forEachOrdered(System.out :: println);
 
        System.out.println("---原来的List1---");
        list1.parallelStream().forEachOrdered(System.out :: println);
        System.out.println("---原来的List2---");
        list2.parallelStream().forEachOrdered(System.out :: println);
 
    }
}

然后这位网友照着 demo 代码修改一番,发现不行。

虽然上面的 demo 运行是可以的,但是把 List 集合中的 String 变成对象后,实现不了功能。

于是他贴出了自己的代码,List<UserDTO> 替换了 List<String>,其他保持不变。

UserDTO 的定义如下:

@Data
public class UserDTO{
    private String id;
    private String name;
    private Integer age;
}

为什么 String 变成 UserDTO 后不行了呢?

原因是,List 的 contains 方法用的比较是 equals,UserDTO 没有自己重写 equals 方法,因此默认的 equals 比较的是继承 Object 类提供的 equals。Object 中 equals 方法比较的是两个引用的内存地址。因此,contains 后发现对象都不相等,全是差集。

那么现在的解决方法有两种,一种是让 UserDTO 类自己重写 equals 方法。

还有一种是先找出要比较的 id,或者 name,再取对象。

第一种过于简单,我就不说了,我主要说一说,第二种做法。

package com.stream;
 
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
 
/**
 *
 * @ClassName: TwoListCopare
 * @Description: 两个List<对象>取交集\并集\差集</>
 **/
public class TwoListCopare {
    public static void main(String[] args) {
        UserDTO userOld1 = new UserDTO("1","aaa",22);
        UserDTO userOld2 = new UserDTO("2","bbb",32);
        UserDTO userOld3 = new UserDTO("3","ccc",11);
        UserDTO userOld4 = new UserDTO("4","ddd",42);
        UserDTO userOld5 = new UserDTO("5","bbb",22);
        UserDTO userOld6 = new UserDTO("6","eee",24);
 
        UserDTO userNew1 = new UserDTO("7","dada",22);     //新增一个
        UserDTO userNew2 = new UserDTO("2","bbb",32);     //不变一个
        UserDTO userNew3 = new UserDTO("3","kaka",33);     //更新一个
        UserDTO userNew4 = new UserDTO("8","dbdb",42);     //新增一个
        UserDTO userNew5 = new UserDTO("5","bbb",100);     //更新一个
        //当然,少了1,4,6
 
        List<UserDTO> mapAdd = new ArrayList<>();
 
        List<UserDTO> oldList = new ArrayList<>();
        List<UserDTO> newList = new ArrayList<>();
 
        //添加老数据
        oldList.add(userOld1);
        oldList.add(userOld2);
        oldList.add(userOld3);
        oldList.add(userOld4);
        oldList.add(userOld5);
        oldList.add(userOld6);
        //添加新数据
        newList.add(userNew1);
        newList.add(userNew2);
        newList.add(userNew3);
        newList.add(userNew4);
        newList.add(userNew5);
 
        //去交集,既获取id相同的交集,需要更新
        //1.先提取出id和结果,用map形式
        List<String> oldIds = new ArrayList<>();
        List<String> newIds = new ArrayList<>();
        oldList.stream().forEach(it->oldIds.add(it.getId()));
        newList.stream().forEach(it->newIds.add(it.getId()));
//        oldIds.stream().forEach(System.out::println);
//        newIds.stream().forEach(System.out::println);
 
        //取交集id
        System.out.println("-----------------交集----------------------");
        List<String> collectUpdate = newIds.stream().filter(it -> oldIds.contains(it)).collect(Collectors.toList());
        collectUpdate.stream().forEach(System.out::println);
        //取对应交集的对象
        System.out.println("------------------交集的对象---------------------");
        List<UserDTO> userUpdate = newList.stream().filter(it -> collectUpdate.contains(it.getId())).collect(Collectors.toList());
        userUpdate.stream().forEach(System.out::println);
 
        //取old的差集
        System.out.println("-----------------old的差集----------------------");
        List<String> collectDelete = oldIds.stream().filter(it -> !newIds.contains(it)).collect(Collectors.toList());
        collectDelete.stream().forEach((System.out::println));
        //取对应old差集对象
        System.out.println("-----------------old差集对象----------------------");
        List<UserDTO> userDelete = oldList.stream().filter(it -> collectDelete.contains(it.getId())).collect(Collectors.toList());
        userDelete.stream().forEach(System.out::println);
 
        //取new的差集
        System.out.println("-----------------new的差集----------------------");
        List<String> collectAdd = newIds.stream().filter(it -> !oldIds.contains(it)).collect(Collectors.toList());
        collectAdd.stream().forEach((System.out::println));
        //取对应old差集对象
        System.out.println("-------------------old差集对象--------------------");
        List<UserDTO> userAdd = newList.stream().filter(it -> collectAdd.contains(it.getId())).collect(Collectors.toList());
        userAdd.stream().forEach(System.out::println);
 
 
        //取并集
        System.out.println("-------------------并集--------------------");
        List<String> allIds = new ArrayList<>();
        //获取一个包含了oldIds和newIds的总结合,但是没有去重
        allIds.addAll(oldIds);
        allIds.addAll(newIds);
        //去重,获取并集ids的新集合
        List<String> joinIds = allIds.stream().distinct().collect(Collectors.toList());
        joinIds.stream().forEach(System.out::println);
    }
}
Java8 Stream 获取两个 List 复杂对象的交集,并集,差集
Java8 Stream 获取两个 List 复杂对象的交集,并集,差集

我这个 demo 中,我取的是 id,大家可以根据需要取不同的字段。或者组合字段,比如,id + name 的形式。

重写 equals 大家一定要注意,hashCode 也需要重写。

@Data
public class UserDTO{
    private String id;
    private String name;
    private Integer age;

    @Override
    public boolean equals(Object obj) {
        if(this == obj){
            return true;//地址相等
        }

        if(obj == null){
            return false;//非空性:对于任意非空引用x,x.equals(null)应该返回false。
        }

        if(obj instanceof UserDTO){
            UserDTO other = (UserDTO) obj;
            //需要比较的字段相等,则这两个对象相等
            if(equalsStr(this.name, other.name)
                    && equalsStr(this.age, other.age)){
                return true;
            }
        }

        return false;
    }

    private boolean equalsStr(String str1, String str2){
        if(StringUtils.isEmpty(str1) && StringUtils.isEmpty(str2)){
            return true;
        }
        if(!StringUtils.isEmpty(str1) && str1.equals(str2)){
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + (name == null ? 0 : name.hashCode());
        result = 31 * result + (age == null ? 0 : age.hashCode());
        return result;
    }
}

以上内容来自群友的智慧,这里分享出来,方便初学者总结知识!

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号1:xmtxtt(5000人已满),微信号2:xttblog(5000人已满),微信号3:codedq(超3800)。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » Java8 Stream 实现复杂集合对象的差集、并集、交集