美文网首页
颠覆认知的Java-lambda流特效:构建管道->数据流转

颠覆认知的Java-lambda流特效:构建管道->数据流转

作者: 小胖学编程 | 来源:发表于2022-02-16 21:24 被阅读0次

本文内容都是个人思索,并未找到官方文档,请大佬评论指正:

需求:对根据对象里面的特定字段,完成对象元素去重;

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 第一步先构建管道,第二步数据才真正流转。

后续可以利用这个特效来提供一个更加便利的工具方法。

相关文章

网友评论

      本文标题:颠覆认知的Java-lambda流特效:构建管道->数据流转

      本文链接:https://www.haomeiwen.com/subject/xogdlrtx.html