Java中两个List之间的比较方法(差集、交集和并集)
作者:程序猿不源 发布时间:2023-03-06 06:34:42
标签: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;
}
来源:https://blog.csdn.net/m0_66782750/article/details/123980925
0
投稿
猜你喜欢
- 双向顺序队列ArrayDeque和双向链式队列LinkedList,JDK已经包含,在此略。ArrayDeque包括顺序栈和顺序队列,Lin
- 近期,公司推行正版化,本人使用的是JetBrains教育版,是不允许进行商业开发的,因此开启了艰难的备用IDE选型之路。最终,我选定了轻量级
- 本章概要返回JSON数据静态资源访问返回JSON数据默认实现JSON 是目前主流的前后端数据传输方式,Spring MVC中使用消息转换器H
- 使用开源项目JAVAE 进行视频格式转换JAVAE简介:JAVE (Java音频视频编码器)库是ffmpeg项目的Java包装器。开发人员可
- 1.使用keySet进行遍历实现步骤:(1)使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中(
- 前言随着网络技术的发展、计算机应用水平广泛提高,原来系统的时效性、数据的正确性、操作的方便性上都存在不足,已影响到系统的正常使用。经过考察比
- 404这个错误真的是一言难尽!不过大多是配置文件出错,认真修改还是可以的1.web.xml配置错误:默认首页没有写的,在web.xml添加一
- Mybatis无法获取带有下划线前缀的字段的值今天下面,把几张表里的字段都加了前缀,如 article_id,article_title,a
- 开始学习WebSocket,准备用它来实现一个在页面实时输出log4j的日志以及控制台的日志。首先知道一些基础信息:1.java7 开始支持
- Java 2D API通过扩展抽象窗口工具箱(AWT),为Java程序提供了二维图像,文本和图形的功能。这个复杂的渲染包支持线形图像,文本和
- 前言springboot提供了 spirng-boot-starter-test 以供开发者使用单元测试,在引入 spring-boot-s
- 1. 什么是λ表达式λ表达式本质上是一个匿名方法。让我们来看下面这个例子: public int add(int x, int
- Timer 详解Timer 和 TimerTask 用于在后台线程中调度任务的 java.
- 1、JavaBean介绍 * JavaBean的定义:JavaBeans是Java中一种特殊的类,可以将多个对象封装到一个对象(bean)
- 一、Jvm加载对象在说Java * 之前,还是要说一下Jvm加载对象的过程,这个依旧是理解 * 的基础性原理:Java类即源代码程序.j
- 一.创建Spring boot项目,添加如下依赖<dependency> <gro
- Spring boot默认使用的是SimpleCacheConfiguration,即使用ConcurrentMapCacheManager
- 众所周知,PDF文档除了具有较强稳定性和兼容性外, 还具有较强的安全性,在工作中可以有效避免别人无意中对文档内容进行修改。但与此同
- 本文实例为大家分享了java使用HashMap实现斗地主的具体代码,供大家参考,具体内容如下案例介绍按照斗地主的规则,完成洗牌发牌的动作。
- 背景Swagger 可以提供 API 操作的测试文档,本文记录 Swagger 使用过程中遇到的两个小问题:全局响应结果进行包装后导致 Sw