List集合去重的一些方法(常规遍历、Set去重、java8 stream去重、重写equals和hashCode方法)

时间:2022-10-28 16:38:14

1. 常规元素去重

碰到List去重的问题,除了遍历去重,我们常常想到利用Set集合不允许重复元素的特点,通过List和Set互转,来去掉重复元素。

    // 遍历后判断赋给另一个List集合,保持原来顺序
public static void ridRepeat1(List<String> list) {
System.out.println("list = [" + list + "]");
List<String> listNew = new ArrayList<String>();
for (String str : list) {
if (!listNew.contains(str)) {
listNew.add(str);
}
}
System.out.println("listNew = [" + listNew + "]");
} // Set集合去重,保持原来顺序
public static void ridRepeat2(List<String> list) {
System.out.println("list = [" + list + "]");
List<String> listNew = new ArrayList<String>();
Set set = new HashSet();
for (String str : list) {
if (set.add(str)) {
listNew.add(str);
}
}
System.out.println("listNew = [" + listNew + "]");
} // Set去重 由于Set(HashSet)的无序性,不会保持原来顺序
public static void ridRepeat3(List<String> list) {
System.out.println("list = [" + list + "]");
Set set = new HashSet();
List<String> listNew = new ArrayList<String>();
set.addAll(list);
listNew.addAll(set);
System.out.println("listNew = [" + listNew + "]");
} // Set通过HashSet去重(将ridRepeat3方法缩减为一行) 无序
public static void ridRepeat4(List<String> list) {
System.out.println("list = [" + list + "]");
List<String> listNew = new ArrayList<String>(new HashSet(list));
System.out.println("listNew = [" + listNew + "]");
} // Set通过TreeSet去重 会按字典顺序重排序
public static void ridRepeat5(List<String> list) {
System.out.println("list = [" + list + "]");
List<String> listNew = new ArrayList<String>(new TreeSet<String>(list));
System.out.println("listNew = [" + listNew + "]");
} // Set通过LinkedHashSet去重 保持原来顺序
public static void ridRepeat6(List<String> list) {
System.out.println("list = [" + list + "]");
List<String> listNew = new ArrayList<String>(new LinkedHashSet<String>(list));
System.out.println("listNew = [" + listNew + "]");
}

除此之外,可以利用java8的stream来实现去重

  //利用java8的stream去重
List uniqueList = list.stream().distinct().collect(Collectors.toList());
System.out.println(uniqueList.toString());

上面的方法在List元素为基本数据类型及String类型时是可以的,但是如果List集合元素为对象,却不会奏效

public class ObjectRidRepeat {

    public static void main(String[] args) {
List<User> userList = new ArrayList<User>();
userList.add(new User("小黄",10));
userList.add(new User("小红",23));
userList.add(new User("小黄",78));
userList.add(new User("小黄",10)); //使用HashSet,无序
Set<User> userSet = new HashSet<User>();
userSet.addAll(userList);
System.out.println(userSet); //使用LinkedHashSet,有序
List<User> listNew = new ArrayList<User>(new LinkedHashSet(userList));
System.out.println(listNew.toString());
}
}

User类结构如下:

List集合去重的一些方法(常规遍历、Set去重、java8 stream去重、重写equals和hashCode方法)

输出如下:(没有去重)

List集合去重的一些方法(常规遍历、Set去重、java8 stream去重、重写equals和hashCode方法)

2. 对象去重

解决对象去重,可以利用for循环遍历的方式进行判断去重,但今天我不准备探究这种方法,要使用的是如下两种:

2.1 使用Java8新特性stream去重

        //根据name属性去重
List<User> unique1 = userList.stream().collect(
collectingAndThen(
toCollection(() -> new TreeSet<>(comparing(User::getName))), ArrayList::new)); System.out.println(unique1.toString()); //根据name,age属性去重
List<User> unique2 = userList.stream().collect(
collectingAndThen(
toCollection(() -> new TreeSet<>(comparing(o -> o.getName() + ";" + o.getAge()))), ArrayList::new)
); System.out.println(unique2.toString());

输出如下:

List集合去重的一些方法(常规遍历、Set去重、java8 stream去重、重写equals和hashCode方法)

2.2 对象中重写equals()方法和hashCode()方法

在User类中重写equals()方法和hashCode()方法:

 //重写equals方法
@Override
public boolean equals(Object obj) {
User user = (User) obj;
return name.equals(user.getName()) && (age==user.getAge());
} //重写hashCode方法
@Override
public int hashCode() {
String str = name + age;
return str.hashCode();
}

当再次执行通过Set去重的方法时,输出如下:

List集合去重的一些方法(常规遍历、Set去重、java8 stream去重、重写equals和hashCode方法)

3. equals()方法和hashCode()方法探究

通过最具代表的的String中的equals()方法和hashCode()方法源码来探究两个方法的实现

3.1 equals()方法

/**
* Compares this string to the specified object. The result is {@code
* true} if and only if the argument is not {@code null} and is a {@code
* String} object that represents the same sequence of characters as this
* object.
*
* @param anObject
* The object to compare this {@code String} against
*
* @return {@code true} if the given object represents a {@code String}
* equivalent to this string, {@code false} otherwise
*
* @see #compareTo(String)
* @see #equalsIgnoreCase(String)
*/
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

比较两个对象时,首先先去判断两个对象是否具有相同的地址,如果是同一个对象的引用,则直接放回true;如果地址不一样,则证明不是引用同一个对象,接下来就是挨个去比较两个字符串对象的内容是否一致,完全相等返回true,否则false。

3.2 hashCode()方法

 /**
* Returns a hash code for this string. The hash code for a
* {@code String} object is computed as
* <blockquote><pre>
* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
* </pre></blockquote>
* using {@code int} arithmetic, where {@code s[i]} is the
* <i>i</i>th character of the string, {@code n} is the length of
* the string, and {@code ^} indicates exponentiation.
* (The hash value of the empty string is zero.)
*
* @return a hash code value for this object.
*/
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value; for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}

当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

根据《Effective Java》第二版的第九条:覆盖equals时总要覆盖hashCode 中的内容,总结如下:

  • 在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一地返回同一个整数。
  • 如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。
  • 如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。

《Java编程思想》中也有类似总结:

  设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果在讲一个对象用put()添加进HashMap时产生一个hashCdoe值,而用get()取出时却产生了另一个hashCode值,那么就无法获取该对象了。所以如果你的hashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码。

List集合去重的一些方法(常规遍历、Set去重、java8 stream去重、重写equals和hashCode方法)的更多相关文章

  1. Java中&equals;&equals;、equals、hashcode的区别与重写equals以及hashcode方法实例&lpar;转&rpar;

    Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例  原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...

  2. 为什么要重写equals和hashcode方法

    equals hashcode  当新建一个java类时,需要重写equals和hashcode方法,大家都知道!但是,为什么要重写呢? 需要保证对象调用equals方法为true时,hashcode ...

  3. 如何正确的重写equals&lpar;&rpar; 和 hashCode&lpar;&rpar;方法

    比较两个Java对象时, 我们需要覆盖equals和  hashCode. public class User{ private String name; private int age; priva ...

  4. java重写equals和hashCode方法

    一.重写equals方法 如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等. 利用equals比较八大包装对象(如int,f ...

  5. 【转】Java中&equals;&equals;、equals、hashcode的区别与重写equals以及hashcode方法实例

    原文地址:http://www.cnblogs.com/luankun0214/p/4421770.html 感谢网友的分享,记录下来只为学习. 1.重写equals方法实例   部分代码参考http ...

  6. Java中&equals;&equals;、equals、hashcode的区别与重写equals以及hashcode方法实例

    1.重写equals方法实例   部分代码参考http://blog.csdn.net/wangloveall/article/details/7899948 重写equals方法的目的是判断两个对象 ...

  7. 内存泄漏避雷!你真的了解重写equals&lpar;&rpar;和hashcode&lpar;&rpar;方法的原因吗&quest;

    基本概念 要比较两个对象是否相等时需要调用对象的equals() 方法: 判断对象引用所指向的对象地址是否相等 对象地址相等时, 那么对象相关的数据也相等,包括: 对象句柄 对象头 对象实例数据 对象 ...

  8. Java 重写equals&lpar;&rpar;与hashCode&lpar;&rpar;方法

    List对象的contains方法实际上也是调用的equals()方法来进行逐条对比的. 示例代码: package com.imooc.collection; /** * 课程类 */ public ...

  9. 重写equals&lpar;&rpar; 和 hashCode&lpar;&rpar;方法

    什么情况下需要重写呢? 比如去重操作时, 有时候往Set集合存放对象User,我们User类的字段太多时,比如有50个字段, 判断两个User对象相同,不需要判断它们所有字段都相同,只需要判断它们的某 ...

随机推荐

  1. Oracle 删除重复数据只留一条

    查询及删除重复记录的SQL语句   1.查找表中多余的重复记录,重复记录是根据单个字段(Id)来判断   select * from 表 where Id in (select Id from 表 g ...

  2. 2016huasacm暑假集训训练三 C - Til the Cows Come Home

    题目链接:http://acm.hust.edu.cn/vjudge/contest/123674#problem/C N题目大意是有n个点,然后给出从a点到b点的距离,a和b是互相可以抵达的,则是无 ...

  3. new work

    果不其然,还是电子工程师适合我.

  4. 山东理工大学ACM平台题答案关于C语言 1181 C语言实验——最小公倍数和最大公约数

    C语言实验——最小公倍数和最大公约数 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 从键盘输入两个正整数,求这两个正整数的最小公 ...

  5. 九度OJ 1511 从尾到头打印链表

    题目地址:http://ac.jobdu.com/problem.php?pid=1511 题目描述: 输入一个链表,从尾到头打印链表每个节点的值. 输入: 每个输入文件仅包含一组测试样例. 每一组测 ...

  6. android-个性化进度条

    1.案例效果图 2.准备素材                                progress1.png(78*78)              progress2.png(78*78) ...

  7. AngularJS中的&dollar;http&period;post与jQuery&period;post的区别

    原文:http://my.oschina.net/tommyfok/blog/287748 很多时候我们需要用ajax提交post数据,angularjs与jq类似,也有封装好的post. 但是jQu ...

  8. 百度网盘免VIP全速下载!

    不知道大家在用百度网盘下载文件时会不会遇到这样一个问题: 过分! 太过分了! 100M的宽带你就给我限速到20KB/s... 当然 解决办法有很多 1.充钱(这辈子都不可能的) ······ 百度上有 ...

  9. JAVA追加写入文本文件

    public void method1() { FileWriter fw = null; try { //如果文件存在,则追加内容:如果文件不存在,则创建文件 File f=new File(&qu ...

  10. MySql之修改操作与进阶

    一:更新特定行 UPDATE tableName SET 列名 = 值,列名 = 值... WHERE 条件; 二:使用子查询更新数据 UPDATE tableName SET 列名 = SELECT ...