如何从两个List中筛选出相同的值
时间:2022-05-04
本文章向大家介绍如何从两个List中筛选出相同的值,主要内容包括问题、模型、最简单的办法:遍历、采用Hash、Hash一定会比遍历快吗、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
问题
现有社保卡和身份证若干,想要匹配筛选出一一对应的社保卡和身份证。 转换为List<社保卡> socialList,和List idList,从二者中找出匹配的社保卡。
模型
创建社保卡类
/**
* @author Ryan Miao
*/
class SocialSecurity{
private Integer id;//社保号码
private Integer idCard;//身份证号码
private String somethingElse;
public SocialSecurity(Integer id, Integer idCard, String somethingElse) {
this.id = id;
this.idCard = idCard;
this.somethingElse = somethingElse;
}
public Integer getId() {
return id;
}
public Integer getIdCard() {
return idCard;
}
public String getSomethingElse() {
return somethingElse;
}
@Override
public String toString() {
return "SocialSecurity{" +
"id=" + id +
", idCard=" + idCard +
", somethingElse='" + somethingElse + ''' +
'}';
}
}
创建身份证类
class IdCard {
private Integer id;//身份证号码
private String somethingElse;
public IdCard(Integer id, String somethingElse) {
this.id = id;
this.somethingElse = somethingElse;
}
public Integer getId() {
return id;
}
public String getSomethingElse() {
return somethingElse;
}
@Override
public String toString() {
return "IdCard{" +
"id=" + id +
", somethingElse='" + somethingElse + ''' +
'}';
}
}
最简单的办法:遍历
只要做两轮循环即可。 准备初始化数据:
private ArrayList<SocialSecurity> socialSecurities;
private ArrayList<IdCard> idCards;
@Before
public void setUp(){
socialSecurities = Lists.newArrayList(
new SocialSecurity(1, 12, "小明"),
new SocialSecurity(2, 13, "小红"),
new SocialSecurity(3, 14, "小王"),
new SocialSecurity(4, 15, "小peng")
);
idCards = Lists.newArrayList(
new IdCard(14, "xiaopeng"),
new IdCard(13, "xiaohong"),
new IdCard(12, "xiaoming")
);
//目标: 从socialSecurities中筛选出idCards中存在的卡片
}
遍历
@Test
public void testFilterForEach(){
List<SocialSecurity> result = new ArrayList<>();
int count = 0;
for (SocialSecurity socialSecurity : socialSecurities) {
for (IdCard idCard : idCards) {
count++;
if (socialSecurity.getIdCard().equals(idCard.getId())){
result.add(socialSecurity);
}
}
}
System.out.println(result);
System.out.println(count);//12 = 3 * 4
//O(m,n) = m*n;
}
很容易看出,时间复杂度O(m,n)=m*n.
采用Hash
通过观察发现,两个list取相同的部分时,每次都遍历两个list。那么,可以把判断条件放入Hash中,判断hash是否存在来代替遍历查找。
@Test
public void testFilterHash(){
Set<Integer> ids = idCards
.stream()
.map(IdCard::getId)
.collect(Collectors.toSet());
List<SocialSecurity> result = socialSecurities
.stream()
.filter(e->ids.contains(e.getIdCard()))
.collect(Collectors.toList());
System.out.println(result);
//初始化 hash 3
//遍历socialSecurities 4
//从hash中判断key是否存在 4
//O(m,n)=2m+n=11
}
如此,假设hash算法特别好,hash的时间复杂度为O(n)=n。如此推出这种做法的时间复杂度为O(m,n)=2m+n. 当然,更重要的是这种写法更让人喜欢,天然不喜欢嵌套的判断,喜欢扁平化的风格。
Hash一定会比遍历快吗
想当然的以为,hash肯定会比遍历快,因为是hash啊。其实,可以算算比较结果。比较什么时候2m+n < m*n
。
从数据归纳法的角度,n必须大于2,不然即演变程2m+2 < 2m
。于是,当n>2时:
@Test
public void testCondition(){
int maxN = 0;
for (int m = 2; m < 100; m++) {
for (int n = 3; n < 100; n++) {
if ((2*m+n)>m*n){
System.out.println("m="+m +",n="+n);
if (n>maxN){
maxN = n;
}
}
}
}
System.out.println(maxN);
}
结果是:
m=2,n=3
3
也就是说n<=3的时候,遍历要比hash快。事实上还要更快,因为hash还需要创建更多的对象。然而,大部分情况下,n也就是第二个数组的长度是大于3的。这就是为什么说hash要更好写。当然,另一个很重要的原因是lambda stream的运算符号远比嵌套循环让人喜爱。
- 一条简单的sql在11g和12c中的不同(r5笔记第2天)
- 浅析 SpringMVC 原理和配置.
- 使用impdp不当导致的数据丢失问题(r5笔记第1天)
- MySQL数据库 Event 定时执行任务.
- Tomcat 日志分割.
- crontab导致的频繁发送邮件的问题(r5笔记第20天)
- 再学习之Spring(依赖注入).
- 使用序列的问题ORA-02287(r5笔记第19天)
- Java多线程详解2
- Java多线程详解3
- SpringMVC处理multipart请求.
- 一条简单的sql语句运行15天的原因分析(r5笔记第17天)
- 巧用flashback database实现灵活的数据回滚(r5笔记第16天)
- Spring Cache For Redis.
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 设计模式实战-模板模式,代码复用神器
- CSS Flexbox 布局完全指南 (二):3000字,多示例讲解
- 设计模式实战-访问者模式,减少代码体积
- Spring Security 实战干货:UsernamePasswordAuthenticationFilter 源码分析
- 打卡群刷题总结0715——旋转链表
- 设计模式实战-中介模式,为你牵桥搭线
- 设计模式实战-迭代器模式,最常用的设计模式之一
- 设计模式实战-解释器模式,今天给你解疑答惑
- 时间不再浪费评估上!ThingJS 3D可视化开发不用愁
- 设计模式实战-命令模式
- 设计模式实战-责任链模式,超级实用
- 设计模式实战-代理模式,来看看主公如何托孤
- 设计模式实战-门面模式
- 设计模式实战-装饰器模式,教你怎么为代码添砖加瓦
- 设计模式实战-组合模式