Java中两个List之间的比较方法(差集、交集和并集)

 更新时间:2022年06月16日 09:20:17   作者:程序猿不源  
在业务的开发过程中会经常用到两个List集合相互取值的情况,下面这篇文章主要给大家介绍了关于Java中两个List之间的比较方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

实现比较两个List之间的差异,包括获取两List的差集,交集,并集(不去重&去重)的API解法和优化解法的解决方案。

求差集

/**
 * 差集(基于API解法) 适用于小数据量
 * 求List1中有的但是List2中没有的元素
 * 时间复杂度 O(list1.size() * list2.size())
 */
public static List<String> subList(List<String> list1, List<String> list2) {
    list1.removeAll(list2);
    return list1;
}
 
/**
 * 差集(基于常规解法)优化解法1 适用于中等数据量
 * 求List1中有的但是List2中没有的元素
 * 空间换时间降低时间复杂度
 * 时间复杂度O(Max(list1.size(),list2.size()))
 */
public static List<String> subList1(List<String> list1, List<String> list2) {
    //空间换时间 降低时间复杂度
    Map<String, String> tempMap = new HashMap<>();
    for(String str:list2){
        tempMap.put(str,str);
    }
    //LinkedList 频繁添加删除 也可以ArrayList容量初始化为List1.size(),防止数据量过大时频繁扩容以及数组复制
    List<String> resList = new LinkedList<>();
    for(String str:list1){
        if(!tempMap.containsKey(str)){
            resList.add(str);
        }
    }
    return resList;
}
 
/**
 * 差集(基于java8新特性)优化解法2 适用于大数据量
 * 求List1中有的但是List2中没有的元素
 */
public static List<String> subList2(List<String> list1, List<String> list2) {
    Map<String, String> tempMap = list2.parallelStream().collect(Collectors.toMap(Function.identity(), Function.identity(), (oldData, newData) -> newData));
    return list1.parallelStream().filter(str->{
        return !tempMap.containsKey(str);
    }).collect(Collectors.toList());
}

求交集

/**
 * 交集(基于API解法) 适用于小数据量
 * 求List1和List2中都有的元素
 * 时间复杂度 O(list1.size() * list2.size())
 */
public static List<String> intersectList(List<String> list1, List<String> list2){
    list1.retainAll(list2);
    return list1;
}
/**
 * 交集(基于常规解法) 优化解法1  适用于中等数据量
 * 求List1和List2中都有的元素
 * 时间复杂度O(Max(list1.size(),list2.size()))
 */
public static List<String> intersectList1(List<String> list1, List<String> list2){
    //空间换时间 降低时间复杂度
    Map<String, String> tempMap = new HashMap<>();
    for(String str:list2){
        tempMap.put(str,str);
    }
    //LinkedList 频繁添加删除 也可以ArrayList容量初始化为List1.size(),防止数据量过大时频繁扩容以及数组复制
    List<String> resList = new LinkedList<>();
    for(String str:list1){
        if(tempMap.containsKey(str)){
            resList.add(str);
        }
    }
    return resList;
}
/**
 * 交集(基于java8新特性)优化解法2 适用于大数据量
 * 求List1和List2中都有的元素
 */
public static List<String> intersectList2(List<String> list1, List<String> list2){
    Map<String, String> tempMap = list2.parallelStream().collect(Collectors.toMap(Function.identity(), Function.identity(), (oldData, newData) -> newData));
    return list1.parallelStream().filter(str->{
        return tempMap.containsKey(str);
    }).collect(Collectors.toList());
}

求并集(不去重)

/**
 * 并集(不去重)
 * 合并list1和list2 不考虑去除重复元素
 * 数组扩容 数组copy
 * @param list1
 * @param list2
 * @return
 */
public static List<String> mergeList(List<String> list1, List<String> list2){
    list1.addAll(list2);
    return list1;
}

求并集(去重)

/**
 * 并集(去重) 基于API解法
 * 合并list1和list2 去除重复元素
 * 时间复杂度主要取决于removeAll 取差集 O(list1.size() * list2.size())
 */
public static List<String> distinctMergeList(List<String> list1, List<String> list2){
    //第一步 先求出list1与list2的差集
    list1.removeAll(list2);
    //第二部 再合并list1和list2
    list1.addAll(list2);
    return list1;
}
/**
 * 并集(去重) 基于Java8新特性 适用于大数据量
 * 合并list1和list2 去除重复元素
 */
public static List<String> distinctMergeList1(List<String> list1, List<String> list2){
    //第一步 先求出list1与list2的差集
    list1 = subList2(list1,list2);
    //第二部 再合并list1和list2
    list1.addAll(list2);
    return list1;
}

实际业务场景

根据客户需求,业务提交审核需要很直观的看到此次提交的数据关联产品的状态变更。

第一种情况:新增的渠道授权关联的产品,所有的授权产品均为新增;

第二种情况:已审核通过的渠道授权重新提交授权审核的,要直观的标记出此次提交审核渠道关联授权产品新增了那些,删除了那些,更改了那些等信息;

第三种情况:作废渠道提交的审核要标注出所有的关联授权产品为删除状态。

授权关联产品为申请表单中一对多关联表,前端展示根据数据的不同状态展示不同的样式:

  • 新增授权产品显示为红色
  • 删除授权产品显示为删除线样式(中划线 )
  • 更新授权产品显示标注红色*号

建立关联产品Vo

首先模拟建立一个产品的实体,此处只简单列入几个属性,在比较所关联产品信息是否是变更状态的时候根据实际业务需要需重写 hashCode 和 equals 方法。

class ProductVo{
    private String id;
    private String name;
    //其他属性不在列入
    //数据状态(新增:insert; 更新:update; 删除:delete)
    private String status;
    //get set 省略
    //如有必要重写hashCode equals
}

业务代码实现

业务实现主要通过 空间换时间 方式降低时间复杂度,先把List转为Map,利用map的 get 和 containsKey 方法理想情况下O(1)的时间复杂度降低嵌套的两次List遍历。

/**
 * 渠道授权新提交关联授权产品 与 历史已审批授权信息对比处理标注授权产品的状态信息<br/>
 * 前端可以根据不同的数据状态显示不同的样式<br/>
 * 用于审核人员直接看到此次提交审核新增了那些授权,取消了那些授权,更改了那些授权
 * @param oldList  原始关联授权产品列表
 * @param newList  提交关联授权产品列表
 * @return
 */
public List<ProductVo> productStatusHandle(List<ProductVo> oldList,List<ProductVo> newList){
    //原始关联授权产品为空 并且 新关联授权产品为空(基本不存在此场景)
    if((oldList == null || oldList.isEmpty()) && (newList == null || newList.isEmpty())){
        return Collections.emptyList();
    }
    //原始关联授权产品为空 则提交关联授权产品全部为新增
    if(oldList == null || oldList.isEmpty()){
        return newList.stream().map(vo->{
            vo.setStatus("insert");
            return vo;
        }).collect(Collectors.toList());
    }
    //提交关联授权产品为空 则删除之前所有的产品授权
    if(newList == null || newList.isEmpty()){
        return oldList.stream().map(vo->{
            vo.setStatus("delete");
            return vo;
        }).collect(Collectors.toList());
    }
    //原始关联授权产品与此次提交关联授权产品均不为空
    List<ProductVo> resList = new LinkedList<>();
    //空间换时间 降低时间复杂度
    //说明:list中不会存在重复(ID相同)的授权产品 否则此toMap收集会抛出异常
    Map<String, ProductVo> oldMap = oldList.stream().collect(Collectors.toMap(ProductVo::getId, Function.identity()));
    Map<String, ProductVo> newMap = newList.stream().collect(Collectors.toMap(ProductVo::getId, Function.identity()));
    for(ProductVo vo:newList){
        ProductVo productVo = oldMap.get(vo.getId());
        //提交关联授权产品在原始关联授权产品
        if(productVo != null){
            if(!vo.equals(productVo)){//重写hashCode与equals自定义规则 用于判定是否数据更新
                vo.setStatus("update");
            }
        }else{//提交审核数据不在旧数据之列
            vo.setStatus("insert");
        }
        resList.add(vo);
    }
    //原始关联授权产品是否存在已取消的情况
    for(ProductVo vo:oldList){
        if(!newMap.containsKey(vo.getId())){
            vo.setStatus("delete");
            resList.add(vo);
        }
    }
    return resList;
}

总结

到此这篇关于Java中两个List之间的比较方法的文章就介绍到这了,更多相关Java中List比较内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java 异步编程实践_动力节点Java学院整理

    Java 异步编程实践_动力节点Java学院整理

    异步编程提供了一个非阻塞的,事件驱动的编程模型。下面通过本文给大家介绍Java 异步编程实践,感兴趣的的朋友一起看看吧
    2017-05-05
  • SpringBoot整合Activiti工作流框架的使用

    SpringBoot整合Activiti工作流框架的使用

    本文主要介绍了SpringBoot整合Activiti工作流框架的使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • 一文详解Spring security框架的使用

    一文详解Spring security框架的使用

    Spring Security是一个基于Spring框架的安全认证和授权框架,它提供了一套全面的安全解决方案,可以在Web应用、移动应用和Web服务等不同场景下使用。本文就来详细聊聊它的使用吧
    2023-03-03
  • mybatis插入后返回主键id的3种方式图解

    mybatis插入后返回主键id的3种方式图解

    这篇文章主要给大家介绍了关于mybatis插入后返回主键id的3种方式,很多时候,在向数据库插入数据时,需要保留插入数据的,以便进行后续的操作或者将存入其他表作为外键,需要的朋友可以参考下
    2023-08-08
  • SpringBoot学习系列之MyBatis Plus整合封装的实例详解

    SpringBoot学习系列之MyBatis Plus整合封装的实例详解

    MyBatis-Plus是一款MyBatis的增强工具(简称MP),为简化开发、提高效率,这篇文章给大家介绍MyBatis Plus整合封装的实例详解,感兴趣的朋友跟随小编一起看看吧
    2020-08-08
  • java实现简单的加减乘除计算器

    java实现简单的加减乘除计算器

    这篇文章主要为大家详细介绍了java实现简单的加减乘除计算器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 锁超时发现parallelStream并行流线程上下文坑解决

    锁超时发现parallelStream并行流线程上下文坑解决

    这篇文章主要为大家介绍了锁超时发现parallelStream并行流线程上下文坑解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Java练习之潜艇小游戏的实现

    Java练习之潜艇小游戏的实现

    这篇文章主要和大家分享一个Java小练习——利用Java编写一个潜艇小游戏,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-03-03
  • 使用javaMail实现发送邮件

    使用javaMail实现发送邮件

    这篇文章主要为大家详细介绍了使用javaMail实现发送邮件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 关于Java反射机制 你需要知道的事情

    关于Java反射机制 你需要知道的事情

    这篇文章主要介绍了Java反射机制的相关内容,涉及了class类的动态加载,获取成员变量、构造函数信息等信息,需要的朋友可以参考下。
    2017-09-09

最新评论