使用Set集合对List集合进行去重
前段时间正好遇到这样一个需求:我们的支付系统从对方系统得到存储明细对象的List集合,存储的明细对象对象的明细类简化为如下TradeDetail类,需求是这样的,我要对称List集合进行去重,这里的去重的意思是只要对象对象中的accountNo账号是相同的,就认为明细对象是相同的,去重之后要求是List集合或者Set集合。
在进行上面的需求对象去重之前,先来看很简单的List集合去重:
package com.qdfae.jdk.collections;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import org.junit.Test;
import com.qdfae.jdk.domain.TradeDetail;
import com.qdfae.jdk.domain.User;
/**
* 使用Set集合对List集合进行去重
*
* @author hongwei.lian
* @date 2018年3月9日 下午11:15:52
*/
public class SetTest {
/**
* List集合的泛型为Integer类型
*
* @author hongwei.lian
* @date 2018年3月9日 下午11:32:53
*/
@Test
public void testListToSet1() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(1);
Set<Integer> set = new HashSet<>(list);
System.out.println("list的个数为:" + list.size() + "个");
list.forEach(System.out::println);
System.out.println("set的个数为:" + set.size() + "个");
set.forEach(System.out::println);
}
/**
* List集合的泛型为String类型
*
* @author hongwei.lian
* @date 2018年3月9日 下午11:34:15
*/
@Test
public void testListToSet2() {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("a");
Set<String> set = new HashSet<>(list);
System.out.println("list的个数为:" + list.size() + "个");
list.forEach(System.out::println);
System.out.println("set的个数为:" + set.size() + "个");
set.forEach(System.out::println);
}
/**
* List集合的泛型为自定义类型User
* 需求是userCode一样的便是同一个对象
*
* @author hongwei.lian
* @date 2018年3月10日 上午12:32:12
*/
@Test
public void testListToSet3() {
List<User> list = new ArrayList<>();
list.add(new User(1,"用户一","600001"));
list.add(new User(2,"用户二","600002"));
list.add(new User(3,"用户一","600001"));
list.add(new User(4,"用户一","600001"));
Set<User> set = new HashSet<>(list);
System.out.println("list的个数为:" + list.size() + "个");
list.forEach(System.out::println);
System.out.println("set的个数为:" + set.size() + "个");
set.forEach(System.out::println);
}
}
上面测试使用到的User类源码:
package com.qdfae.jdk.domain;
import java.io.Serializable;
/**
* User实体类
*
* @author hongwei.lian
* @date 2018年3月10日 上午12:33:22
*/
public class User implements Serializable {
private static final long serialVersionUID = -7629758766870065977L;
/**
* 用户ID
*/
private Integer id;
/**
* 用户姓名
*/
private String userName;
/**
* 用户代码
*/
private String userCode;
public User() {}
public User(Integer id, String userName, String userCode) {
this.id = id;
this.userName = userName;
this.userCode = userCode;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", userCode=" + userCode + "]";
}
}
依次运行上面三个方法的结果是:
testListToSet1()方法结果:
testListToSet2()方法结果:
testListToSet3()方法结果:
上面的testListToSet1()方法和testListToSet2()方法可以去重,那为什么testListToSet3()方法就不能去重呢?仔细想想就会知道,两个对象的地址值不一样,怎么会认为是相同的去重呢,再往深处想,就会想到Object类的hashCode()方法和equals()方法,这两个方法决定了两个对象是否相等。Integer类和String类之所以可以进行去重,是因为这两个类都重写了父类Object类中的hashCode()方法和equals()方法,具体的代码可以去查看JDK源码,这里不再赘述。到这里我们就知道User对象不能去重的原因所在,那么我们根据需求在User类中重写hashCode()方法和equals()方法,重写后的User类源码如下:
package com.qdfae.jdk.domain;
import java.io.Serializable;
/**
* User实体类
*
* @author hongwei.lian
* @date 2018年3月10日 上午12:33:22
*/
public class User implements Serializable {
private static final long serialVersionUID = -7629758766870065977L;
/**
* 用户ID
*/
private Integer id;
/**
* 用户姓名
*/
private String userName;
/**
* 用户代码
*/
private String userCode;
public User() {}
public User(Integer id, String userName, String userCode) {
this.id = id;
this.userName = userName;
this.userCode = userCode;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
/**
* 针对userCode重写hashCode()方法
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((userCode == null) ? 0 : userCode.hashCode());
return result;
}
/**
* 针对userCode重写equals()方法
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (userCode == null) {
if (other.userCode != null)
return false;
} else if (!userCode.equals(other.userCode))
return false;
return true;
}
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", userCode=" + userCode + "]";
}
}
我们再次运行testListToSet3()方法结果:
这一次符合我们的需求,接下里再来看开头提出的需求。
准备:
TradeDetail类源码:
package com.qdfae.jdk.domain;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 交易明细
*
* @author hongwei.lian
* @date 2018年3月10日 下午2:44:35
*/
public class TradeDetail implements Serializable {
private static final long serialVersionUID = 3386554986241170136L;
/**
* 交易明细主键
*/
private Integer id;
/**
* 账号
*/
private String accountNo;
/**
* 账户名称
*/
private String accountName;
/**
* 交易金额(+表示入金,-表示出金)
*/
private BigDecimal balance;
public TradeDetail() {}
public TradeDetail(Integer id, String accountNo, String accountName, BigDecimal balance) {
this.id = id;
this.accountNo = accountNo;
this.accountName = accountName;
this.balance = balance;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAccountNo() {
return accountNo;
}
public void setAccountNo(String accountNo) {
this.accountNo = accountNo;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
/**
* 针对accountNo重写hashCode()方法
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((accountNo == null) ? 0 : accountNo.hashCode());
return result;
}
/**
* 针对accountNo重写equals()方法
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TradeDetail other = (TradeDetail) obj;
if (accountNo == null) {
if (other.accountNo != null)
return false;
} else if (!accountNo.equals(other.accountNo))
return false;
return true;
}
@Override
public String toString() {
return "TradeDetail [id=" + id + ", accountNo=" + accountNo + ", accountName=" + accountName + ", balance="
+ balance + "]";
}
}
我们首先来按照上面的想法根据需求重写TradeDetail类的hashCode()方法和equals()方法,上面已经给出重写后的TradeDetail类。
我有三种实现方案如下:
package com.qdfae.jdk.collections;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test;
import com.qdfae.jdk.domain.TradeDetail;
/**
* List集合去重
*
* @author hongwei.lian
* @date 2018年3月11日 下午8:54:57
*/
public class DuplicateListTest {
/**
* 存储没有去重的明细对象的List集合
*/
private List<TradeDetail> tradeDetailList;
/**
* 存储去重后的明细对象的List集合
*/
private List<TradeDetail> duplicateTradeDetailList;
/**
* 存储去重后的明细对象的Set集合
*/
private Set<TradeDetail> tradeDetailSet;
/**
* 初始化tradeDetailList
*
* @author hongwei.lian
* @date 2018年3月11日 下午9:04:45
*/
@Before
public void InitTradeDetailList() {
tradeDetailList = new ArrayList<>();
tradeDetailList.add(new TradeDetail(1, "600010", "账户一", new BigDecimal(100.00)));
tradeDetailList.add(new TradeDetail(2, "600011", "账户二", new BigDecimal(100.00)));
tradeDetailList.add(new TradeDetail(3, "600010", "账户一", new BigDecimal(-100.00)));
tradeDetailList.add(new TradeDetail(4, "600010", "账户一", new BigDecimal(-100.00)));
}
/**
* 使用Set接口的实现类HashSet进行List集合去重
*
* HashSet实现类
* 构造方法:
* public TreeSet(Comparator<? super E> comparator)
*
* @author hongwei.lian
* @date 2018年3月11日 下午9:37:51
*/
@Test
public void testDuplicateListWithHashSet() {
//-- 前提是TradeDetail根据规则重写hashCode()方法和equals()方法
tradeDetailSet = new HashSet<>(tradeDetailList);
tradeDetailSet.forEach(System.out::println);
}
/**
* 使用Map集合进行List集合去重
*
* @author hongwei.lian
* @date 2018年3月11日 下午9:05:49
*/
@Test
public void testDuplicateListWithIterator() {
duplicateTradeDetailList = new ArrayList<>();
Map<String, TradeDetail> tradeDetailMap = tradeDetailList.stream()
.collect(Collectors.toMap(
tradeDetail -> tradeDetail.getAccountNo(),
tradeDetail -> tradeDetail,
(oldValue, newValue) -> newValue));
tradeDetailMap.forEach(
(accountNo, tradeDetail) -> duplicateTradeDetailList.add(tradeDetail)
);
duplicateTradeDetailList.forEach(System.out::println);
//-- 参考文章
//http://blog.jobbole.com/104067/
//https://www.cnblogs.com/java-zhao/p/5492122.html
}
/**
* 使用Set接口的实现类TreeSet进行List集合去重
*
* TreeSet实现类
* 构造方法:
* public TreeSet(Comparator<? super E> comparator)
*
* @author hongwei.lian
* @date 2018年3月11日 下午9:37:48
*/
@Test
public void testDuplicateListWithTreeSet() {
tradeDetailSet = new TreeSet<>(
(tradeDetail1, tradeDetail2)
->
tradeDetail1.getAccountNo().compareTo(tradeDetail2.getAccountNo())
);
tradeDetailSet.addAll(tradeDetailList);
tradeDetailSet.forEach(System.out::println);
}
}
运行上面三个方法的结果都是:
方案一:根据需求重写自定义类的hashCode()方法和equals()方法
这种方案的不足之处是根据需求重写后的hashCode()方法和equals()方法不一定满足其他需求,这样这个TradeDetail类的复用性就会相当差。
方案二:遍历List集合,取出每一个明细对象,将明细对象的accountNo属性字段作为Map集合key,明细对象作为Map集合的value,然后再遍历Map集合,得到一个去重后的List集合或者Set集合。
这种方案的不足之处是消耗性能,首先是List集合去重转换为Map集合,Map集合再次转换为List集合或者Set集合,遍历也会消耗性能。
方案三:使用TreeSet集合的独有的构造方法进行去重,如下:
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
这种方案目前为止是我使用的比较多的方案,不足之处暂时没有发现,TreeSet集合实际上是利用TreeMap的带有一个比较器参数的构造方法实现,看JDK源码很清晰,最重要的是这个参数Comparator接口,这个接口的源码:
Comparator接口部分源码:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
这个compare()方法需要自己根据需求去实现,仔细看上面去重的原理实际上还是使用String类的compareTo()方法,String类的compareTo()方法源码:
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
基本就想到了这些,如果有好的实现方法,自己还会补充。

使用Set集合对List集合进行去重相关推荐

  1. python集合运算符_Python 集合、字典、运算符

    先区分一下序列类型和散列类型: 序列类型:list.string.tuple,他们中的元素是有序的. 散列类型:set.dict,他们中的元素无序的.(注意:python3.7.0开始字典变成&quo ...

  2. java链表集合_Java底层基于链表实现集合和映射--集合Set操作详解

    本文实例讲述了Java底层基于链表实现集合和映射--集合Set操作.分享给大家供大家参考,具体如下: 在Java底层基于二叉搜索树实现集合和映射中我们实现了底层基于二叉搜索树的集合,本节就底层如何基于 ...

  3. python空集合_python空集合

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! - 不可变数据类型实现某个功能,需要将结果赋值给另外一个变量; 是否实现for循 ...

  4. python集合常用方法_Python 集合常用方法总结

    数据类型:int/str/bool/list/dict/tuple/float/set (set类型天生去重) 一.集合的定义 s = set() #定义空集合 s = {'a','b','c','d ...

  5. [转载] python创建集合set()_Python 集合set()

    参考链接: Python set集合 | difference 创建 # 创建空集合 set() # 创建集合,参数必须为 iterable set(it) set(dict) # 只取dict.ke ...

  6. Java进阶(七)Set系列集合、Map集合体系

    七.Set系列集合.Map集合体系 需要学会什么? Set系列集合的特点:Set系列集合的特点和底层原理. 集合工具类Collections:快速的对集合进行元素的添加.排序等操作. 综合案例:把Co ...

  7. 网易之小易最近在数学课上学习到了集合的概念,集合有三个特征:1.确定性 2.互异性 3.无序性.需要根据给定的w,x,y,z,求出集合中一共有多少个元素。

    import java.util.HashSet; import java.util.Scanner; import java.util.Set;/*** 小易最近在数学课上学习到了集合的概念,集合有 ...

  8. (小甲鱼python)集合笔记合集一 集合(上)总结 集合的简单用法 集合的各种方法合集:子、交、并、补、差、对称差集、超集

    一.基础复习 集合与字典区别 集合中所有元素都是独一无二的,并且也是无序的. 集合具有唯一性.无序性.有限性 >>> type({}) #字典 <class 'dict'> ...

  9. Map集合以及Map集合的实现类Stream流的使用

    Map 遍历Map集合 //方法一 //获得所有的键的Set集合 Set<Object> set = map.ketSet(); for(Object key : set){//根据具体的 ...

  10. Python(IT峰)笔记07-数据类型详解-元祖的定义与操作,元祖推导式,元祖生成器,yield关键字,字典及定义,字典所支持的操作,zip研所函数,dict转型,字典函数,集合,冰冻集合,集合推导

    1.元祖的定义 一组有序数据的组合,元祖一旦定义不可修改,是不可变数据类型 定义空元祖 变量=() 变量=tuple() 变量=(内容1,内容2,内容3,--)直接赋值 特列:变量=内容1,内容2,内 ...

最新文章

  1. ros 消息队列与缓冲区_Spring Boot消息队列系统:RocketMQ初入门
  2. AI与BCI相结合读取大脑数据,根据个人喜好生成图像
  3. @Transactional事务几点注意
  4. 升级GCC 4.6后的warning: ”variable set but not used“
  5. 4che3 scu发送超时设置_Redis实现订阅发布与批量发送短信
  6. createprocess失败代码2_DevOPS | 基于sonarqube、jenkins和gitlab的持续集成代码检查
  7. webserver入门
  8. 6.1 API : AdaBoostClassifier与AdaBoostRegressor
  9. 数学建模(NO.10 典型相关分析)
  10. 技术总监和CTO的区别 浅谈CTO的作用----软件公司如何开源节流
  11. Android基站定位源代码
  12. 计算机更改虚拟内存有用吗,电脑虚拟内存有什么用(小白必知虚拟内存作用及设置技巧)...
  13. 对大一c语言学习的感想
  14. Mysql批量删除大量数据
  15. 数据结构——2-3树
  16. MATLAB颜色的植物虫害检测识别
  17. 前端数据处理——行政区域编码
  18. PHP——人人都会编程
  19. 浅谈用企业微信生态做私域流量运营的策略!
  20. Linux内核国内下载地址

热门文章

  1. 再回顾SGX初始化(三)——uRTS维护Enclave、tRTS完成Enclave构建收尾确认工作
  2. Vue解决跨域问题方案
  3. win7 共享wifi
  4. 添加过滤器后登录界面无法登录的bug--已解决
  5. jvm System.gc()说明
  6. 帝国cms 自动生成html,帝国cms实现用户访问页面自动生成html的方法
  7. 逃跑h5小游戏源码熊出没手机游戏
  8. java excel 批注_Java 添加、读取和删除 Excel 批注
  9. mac为什么不支持ntfs,mac读取ntfs移动硬盘软件有哪些
  10. 数据分析师职业分析报告