List stream 对象 属性去重

it2022-05-05  193

List去重的问题

1. 常规元素去重

private static List<String> list = new ArrayList(); static { list.add("李"); list.add("王"); list.add("王"); list.add("B"); list.add("A"); list.add("A"); list.add("a"); }

遍历去重

// 遍历后判断赋给另一个list集合,保持原来顺序 @Test public void ridRepeat1() { 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 + "]"); } /* list = [[李, 王, 王, B, A, A, a]] listNew = [[李, 王, B, A, a]] */

Set集合不允许重复元素的特点,通过List和Set互转,来去掉重复元素

// set集合去重,保持原来顺序 @Test public void ridRepeat2() { 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 + "]"); } /** list = [[李, 王, 王, B, A, A, a]] listNew = [[李, 王, B, A, a]] */ // Set去重 由于Set的无序性,不会保持原来顺序 @Test public void ridRepeat3() { 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 + "]"); } /* list = [[李, 王, 王, B, A, A, a]] listNew = [[A, a, B, 王, 李]] */ // Set去重(将ridRepeat3方法缩减为一行) 无序 @Test public void ridRepeat4() { System.out.println("list = [" + list + "]"); List<String> listNew = new ArrayList<String>(new HashSet(list)); System.out.println("listNew = [" + listNew + "]"); } /* list = [[李, 王, 王, B, A, A, a]] listNew = [[A, a, B, 王, 李]] */ // Set去重并保持原先顺序的两种方法 @Test public void ridRepeat5() { System.out.println("list = [" + list + "]"); List<String> listNew = new ArrayList<String>(new TreeSet<String>(list)); List<String> listNew2 = new ArrayList<String>(new LinkedHashSet<String>(list)); System.out.println("listNew = [" + listNew + "]"); System.out.println("listNew2 = [" + listNew2 + "]"); } /** list = [[李, 王, 王, B, A, A, a]] listNew = [[A, B, a, 李, 王]] listNew2 = [[李, 王, B, A, a]] */

利用java8 的 stream来实现去重

@Test public void ridRepeat6() { System.out.println("list = [" + list + "]"); List uniqueList = list.stream().distinct().collect(Collectors.toList()); System.out.println("listNew = ["+uniqueList.toString()+"]"); } /** list = [[李, 王, 王, B, A, A, a]] listNew = [[李, 王, B, A, a]] */

2. 对象去重

随手一个对象

static class User { String name; Integer sex; Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getSex() { return sex; } public void setSex(Integer sex) { this.sex = sex; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public User(String name, Integer sex, Integer age) { this.name = name; this.sex = sex; this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", sex=" + sex + ", age=" + age + '}'; } }

List集合元素为对象 之前的方法就不会奏效

@Test public void ridRepeat7() { //使用HashSet,无序 s Set<User> userSet = new HashSet<User>(); userSet.addAll(list2); System.out.println(list2); System.out.println(userSet); /** [User{name='网三', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='a', sex=1, age=18}, User{name='李四', sex=1, age=18}, User{name='王五', sex=1, age=18}, User{name='王五', sex=1, age=18}] [User{name='a', sex=1, age=18}, User{name='王五', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='王五', sex=1, age=18}, User{name='网三', sex=1, age=18}, User{name='李四', sex=1, age=18}] */ //使用LinkedHashSet,有序 System.out.println(list2); List<User> listNew = new ArrayList<User>(new LinkedHashSet(list2)); System.out.println(listNew.toString()); /** [User{name='网三', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='a', sex=1, age=18}, User{name='李四', sex=1, age=18}, User{name='王五', sex=1, age=18}, User{name='王五', sex=1, age=18}] [User{name='网三', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='a', sex=1, age=18}, User{name='李四', sex=1, age=18}, User{name='王五', sex=1, age=18}, User{name='王五', sex=1, age=18}] */ }

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

 

//重写equals方法 @Override public boolean equals(Object obj) { User user = (User) obj; return name.equals(user.getName()) && (age==user.getAge()) && (sex==sex.getAge()); } //重写hashCode方法 @Override public int hashCode() { String str = name + age+ sex; return str.hashCode(); } @Test public void ridRepeat8() { //使用HashSet,无序 s Set<User> userSet = new HashSet<User>(); userSet.addAll(list2); System.out.println(list2); System.out.println(userSet); /** [User{name='网三', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='a', sex=1, age=18}, User{name='李四', sex=1, age=18}, User{name='王五', sex=1, age=18}, User{name='王五', sex=1, age=18}] [User{name='A', sex=1, age=18}, User{name='a', sex=1, age=18}, User{name='网三', sex=1, age=18}, User{name='王五', sex=1, age=18}, User{name='李四', sex=1, age=18}]] */ //使用LinkedHashSet,有序 System.out.println(list2); List<User> listNew = new ArrayList<User>(new LinkedHashSet(list2)); System.out.println(listNew.toString()); /** [User{name='网三', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='a', sex=1, age=18}, User{name='李四', sex=1, age=18}, User{name='王五', sex=1, age=18}, User{name='王五', sex=1, age=18}] [User{name='网三', sex=1, age=18}, User{name='A', sex=1, age=18}, User{name='a', sex=1, age=18}, User{name='李四', sex=1, age=18}, User{name='王五', sex=1, age=18}] */ }

 

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

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

/** * 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。

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()方法就会生成一个不同的散列码。

用Java8新特性stream去重

import static java.util.Comparator.comparing; import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toCollection; @Test public void ridRepeat7() { //根据name属性去重 System.out.println(list2); List<User> unique1 = list2.stream().collect( collectingAndThen( toCollection(() -> new TreeSet<>(comparing(User::getName))), ArrayList::new)); System.out.println(unique1.toString()); //根据name,age属性去重 List<User> unique2 = list2.stream().collect( collectingAndThen( toCollection(() -> new TreeSet<>(comparing(o -> o.getName() + ";" + o.getAge()))), ArrayList::new) ); System.out.println(list2); System.out.println(unique2.toString()); }

最新回复(0)