本文内容都是个人思索,并未找到官方文档,请大佬评论指正:
需求:对根据对象里面的特定字段,完成对象元素去重;
public class Test4 {
static List<User> ans = Lists.newArrayList(new User("a"),
new User("a"), new User("b"), new User("c"));
/**
* 创建的是Predicate对象,而创建代码只会走一次,
*/
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
System.out.println("--->创建seen:" + seen);
return t -> {
System.out.println("filter操作" + t);
return seen.add(keyExtractor.apply(t));
};
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class UserEx {
private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class User {
private String name;
}
}
普通版
普通写法:在外部定义一个Set集合,然后利于set的add方法完成去重。
public static void main(String[] args) {
//流式去重
Set<String> seen = new HashSet<>();
ans.stream().filter(a -> seen.add(a.getName())).collect(Collectors.toList());
}
但是能不能将去重逻辑抽取为一个通用的静态方法:
进阶版
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> seen.add(keyExtractor.apply(t));
}
使用方式:
public static void main(String[] args) {
//流式去重
Set<String> seen = new HashSet<>();
ans.stream().filter(distinctByKey(User::getName)).collect(Collectors.toList());
}
但是细心的小伙伴可以发现,因为Stream流会多次执行filter操作,是不是会多次调用distinctByKey
逻辑,每一次都创建一个Set集合,如何达到去重的效果呢???
打印日志:
public static void main(String[] args) {
//lambda表达式传入java.util.function类型
List<UserEx> collect = ans.stream()
.map(r -> {
System.out.println("前置map操作" + r);
return new UserEx(r.name);
})
.filter(distinctByKey(UserEx::getName))
.peek(r -> System.out.println("后置peek操作" + r))
.collect(Collectors.toList());
}
/**
* 创建的是Predicate对象,而创建代码只会走一次,
*/
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
System.out.println("--->创建seen:" + seen);
return t -> {
System.out.println("filter操作" + t);
return seen.add(keyExtractor.apply(t));
};
}
执行效果:
--->创建seen:[]
前置map操作Test4.User(name=a)
filter操作Test4.UserEx(name=a)
后置peek操作Test4.UserEx(name=a)
前置map操作Test4.User(name=a)
filter操作Test4.UserEx(name=a)
前置map操作Test4.User(name=b)
filter操作Test4.UserEx(name=b)
后置peek操作Test4.UserEx(name=b)
前置map操作Test4.User(name=c)
filter操作Test4.UserEx(name=c)
后置peek操作Test4.UserEx(name=c)
[Test4.UserEx(name=a), Test4.UserEx(name=b), Test4.UserEx(name=c)]
可以看到:在最开始的时候,Set只创建了一次,在执行filter操作时,并未创建Set。
推测:stream 第一步先构建管道,第二步数据才真正流转。
后续可以利用这个特效来提供一个更加便利的工具方法。
网友评论