本文內(nèi)容都是個人思索俏脊,并未找到官方文檔艘包,請大佬評論指正:
需求:對根據(jù)對象里面的特定字段,完成對象元素去重寨典;
public class Test4 {
static List<User> ans = Lists.newArrayList(new User("a"),
new User("a"), new User("b"), new User("c"));
/**
* 創(chuàng)建的是Predicate對象熏迹,而創(chuàng)建代碼只會走一次,
*/
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
System.out.println("--->創(chuàng)建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());
}
但是能不能將去重邏輯抽取為一個通用的靜態(tài)方法:
進(jìn)階版
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());
}
但是細(xì)心的小伙伴可以發(fā)現(xiàn)坛缕,因?yàn)镾tream流會多次執(zhí)行filter操作,是不是會多次調(diào)用distinctByKey
邏輯捆昏,每一次都創(chuàng)建一個Set集合赚楚,如何達(dá)到去重的效果呢?骗卜?宠页?
打印日志:
public static void main(String[] args) {
//lambda表達(dá)式傳入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());
}
/**
* 創(chuàng)建的是Predicate對象,而創(chuàng)建代碼只會走一次寇仓,
*/
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
System.out.println("--->創(chuàng)建seen:" + seen);
return t -> {
System.out.println("filter操作" + t);
return seen.add(keyExtractor.apply(t));
};
}
執(zhí)行效果:
--->創(chuàng)建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只創(chuàng)建了一次,在執(zhí)行filter操作時遍烦,并未創(chuàng)建Set俭嘁。
推測:stream 第一步先構(gòu)建管道,第二步數(shù)據(jù)才真正流轉(zhuǎn)服猪。
后續(xù)可以利用這個特效來提供一個更加便利的工具方法供填。