Java 8 Stream.distinct() 列表去重的操作

时间:2021-05-20

在这篇文章里,我们将提供Java8 Stream distinct()示例。 distinct()返回由该流的不同元素组成的流。distinct()是Stream接口的方法。

distinct()使用hashCode()和equals()方法来获取不同的元素。因此,我们的类必须实现hashCode()和equals()方法。

如果distinct()正在处理有序流,那么对于重复元素,将保留以遭遇顺序首先出现的元素,并且以这种方式选择不同元素是稳定的。

在无序流的情况下,不同元素的选择不一定是稳定的,是可以改变的。distinct()执行有状态的中间操作。

在有序流的并行流的情况下,保持distinct()的稳定性是需要很高的代价的,因为它需要大量的缓冲开销。如果我们不需要保持遭遇顺序的一致性,那么我们应该可以使用通过BaseStream.unordered()方法实现的无序流。

1. Stream.distinct()

distinct()方法的声明如下:

Stream<T> distinct()

它是Stream接口的方法。在此示例中,我们有一个包含重复元素的字符串数据类型列表

DistinctSimpleDemo.java

package com.concretepage;import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;public class DistinctSimpleDemo { public static void main(String[] args) { List<String> list = Arrays.asList("AA", "BB", "CC", "BB", "CC", "AA", "AA"); long l = list.stream().distinct().count(); System.out.println("No. of distinct elements:"+l); String output = list.stream().distinct().collect(Collectors.joining(",")); System.out.println(output); }}

Output

No. of distinct elements:3

AA,BB,CC

2. Stream.distinct() with List of Objects

在此示例中,我们有一个Book对象列表。 为了对列表进行去重,该类将重写hashCode()和equals()。

Book.java

package com.concretepage;public class Book { private String name; private int price; public Book(String name, int price) { this.name = name; this.price = price; } public String getName() { return name; } public int getPrice() { return price; } @Override public boolean equals(final Object obj) { if (obj == null) { return false; } final Book book = (Book) obj; if (this == book) { return true; } else { return (this.name.equals(book.name) && this.price == book.price); } } @Override public int hashCode() { int hashno = 7; hashno = 13 * hashno + (name == null ? 0 : name.hashCode()); return hashno; }}

DistinctWithUserObjects.java

package com.concretepage;import java.util.ArrayList;import java.util.List;public class DistinctWithUserObjects { public static void main(String[] args) { List<Book> list = new ArrayList<>(); { list.add(new Book("Core Java", 200)); list.add(new Book("Core Java", 200)); list.add(new Book("Learning Freemarker", 150)); list.add(new Book("Spring MVC", 300)); list.add(new Book("Spring MVC", 300)); } long l = list.stream().distinct().count(); System.out.println("No. of distinct books:"+l); list.stream().distinct().forEach(b -> System.out.println(b.getName()+ "," + b.getPrice())); }}

Output

No. of distinct books:3Core Java,200Learning Freemarker,150Spring MVC,300

3. Distinct by Property

distinct()不提供按照属性对对象列表进行去重的直接实现。它是基于hashCode()和equals()工作的。

如果我们想要按照对象的属性,对对象列表进行去重,我们可以通过其它方法来实现。

如下代码段所示:

static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) { Map<Object,Boolean> seen = new ConcurrentHashMap<>(); return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;}

上面的方法可以被Stream接口的 filter()接收为参数,如下所示:

list.stream().filter(distinctByKey(b -> b.getName()));

distinctByKey()方法返回一个使用ConcurrentHashMap 来维护先前所见状态的 Predicate 实例,如下是一个完整的使用对象属性来进行去重的示例。

DistinctByProperty.java

package com.concretepage;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import java.util.function.Function;import java.util.function.Predicate;public class DistinctByProperty { public static void main(String[] args) { List<Book> list = new ArrayList<>(); { list.add(new Book("Core Java", 200)); list.add(new Book("Core Java", 300)); list.add(new Book("Learning Freemarker", 150)); list.add(new Book("Spring MVC", 200)); list.add(new Book("Hibernate", 300)); } list.stream().filter(distinctByKey(b -> b.getName())) .forEach(b -> System.out.println(b.getName()+ "," + b.getPrice())); } private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) { Map<Object,Boolean> seen = new ConcurrentHashMap<>(); return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; }}

Output

Core Java,200Learning Freemarker,150Spring MVC,200Hibernate,300

from : https://.example.demo.dto.User;import java.util.*;import java.util.stream.Collectors;public class FirCes { public static void main(String[] args) { User user1 = new User("a小张1", "a1", 10); User user2 = new User("b小张2", "a2", 10); User user3 = new User("c小张3", "a3", 10); User user3_3 = new User("c小张3", "a", 10); User user33 = new User("c小张3", "a3", 10); User user4 = new User("d小张4", "a4", 10); User user5 = new User("e小张5", "a5", 10); List<User> list = new ArrayList<>(); list.add(user1); list.add(user2); list.add(user3); list.add(user3_3); list.add(user33); list.add(user4); list.add(user5); //按相同name与address属性分组User用户 Map<User, List<User>> listMap = list.stream().collect(Collectors.groupingBy(v -> v)); listMap.forEach((key, value) -> { System.out.println("========"); System.out.println("key:" + key); value.forEach(obj -> { System.out.println(obj); }); }); List<User> listNew = listMap.keySet().stream().map(u -> { int sum = listMap.get(u).stream().mapToInt(i -> i.getAge()).sum(); //需要注意的是:这里也会改变原list集合中的原数据。因为这里的u分组时就是来自原集合中的一个地址对象, // 即:指向了原集合中的一个对象的地址。如果不想原集合被影响,这里可以new User()新的对象赋值并返回新对象 u.setAge(sum); return u; }).collect(Collectors.toList()); System.out.println("listNew:" + listNew); System.err.println("list:" + list); //但是一个实体类只能重写一次equals方法,如果有多种判别需求就不好满足了, // 可以定义多个不同类名相同属性的类或者下面这种方式解决 Map<String, List<User>> listMap1 = list.stream().collect(Collectors .groupingBy(v -> Optional.ofNullable(v.getName()).orElse("") + "_" + Optional.ofNullable(v.getAddress()).orElse(""))); listMap1.forEach((key, value) -> { System.out.println("========"); System.out.println("key:" + key); value.forEach(obj -> { System.out.println(obj); }); }); List<User> listNew1 = listMap1.keySet().stream().map(u -> { int sum = listMap1.get(u).stream().mapToInt(i -> i.getAge()).sum(); User user = listMap1.get(u).get(0); //这里和上面一样的原理,也会影响原list集合中的被指向的地址的对象数据 user.setAge(sum); return user; }).collect(Collectors.toList()); System.out.println("listNew1:" + listNew1); System.err.println("list:" + list); } }

打印日志:

========key:User{name='b小张2', address='a2', age=10}User{name='b小张2', address='a2', age=10}========key:User{name='c小张3', address='a', age=10}User{name='c小张3', address='a', age=10}========key:User{name='c小张3', address='a3', age=10}User{name='c小张3', address='a3', age=10}User{name='c小张3', address='a3', age=10}========key:User{name='a小张1', address='a1', age=10}User{name='a小张1', address='a1', age=10}========key:User{name='d小张4', address='a4', age=10}User{name='d小张4', address='a4', age=10}========key:User{name='e小张5', address='a5', age=10}User{name='e小张5', address='a5', age=10}listNew:[User{name='b小张2', address='a2', age=10}, User{name='c小张3', address='a', age=10}, User{name='c小张3', address='a3', age=20}, User{name='a小张1', address='a1', age=10}, User{name='d小张4', address='a4', age=10}, User{name='e小张5', address='a5', age=10}]list:[User{name='a小张1', address='a1', age=10}, User{name='b小张2', address='a2', age=10}, User{name='c小张3', address='a3', age=20}, User{name='c小张3', address='a', age=10}, User{name='c小张3', address='a3', age=10}, User{name='d小张4', address='a4', age=10}, User{name='e小张5', address='a5', age=10}]========key:a小张1_a1User{name='a小张1', address='a1', age=10}========key:c小张3_aUser{name='c小张3', address='a', age=10}========key:d小张4_a4User{name='d小张4', address='a4', age=10}========key:e小张5_a5User{name='e小张5', address='a5', age=10}========key:b小张2_a2User{name='b小张2', address='a2', age=10}========key:c小张3_a3User{name='c小张3', address='a3', age=20}User{name='c小张3', address='a3', age=10}listNew1:[User{name='a小张1', address='a1', age=10}, User{name='c小张3', address='a', age=10}, User{name='d小张4', address='a4', age=10}, User{name='e小张5', address='a5', age=10}, User{name='b小张2', address='a2', age=10}, User{name='c小张3', address='a3', age=30}]list:[User{name='a小张1', address='a1', age=10}, User{name='b小张2', address='a2', age=10}, User{name='c小张3', address='a3', age=30}, User{name='c小张3', address='a', age=10}, User{name='c小张3', address='a3', age=10}, User{name='d小张4', address='a4', age=10}, User{name='e小张5', address='a5', age=10}]Process finished with exit code 0

以上这篇Java 8 Stream.distinct() 列表去重的操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章