【问题标题】:Difference between implementation of functional interface, used along with streams与流一起使用的功能接口实现之间的区别
【发布时间】:2020-07-24 23:49:15
【问题描述】:

我想在这里了解过滤流的工作原理。 以下是从ListStrings 中过滤掉重复项的两种方法。 (我不想使用 distinct 关键字进行演示)

List<String> list = new ArrayList(){{add("abc");add("abc");add("abcd");}};
List<String> list1 = list.parallelStream().filter(e->{
                                           Set<String> seen = ConcurrentHashMap.newKeySet();
                                           return seen.add(e.getLastName());
                    }).collect(Collectors.toList());

上述方法似乎不起作用,因为每次都在创建 Set 的新实例。

List<String> list2  =    
list.parallelStream().filter(distincyByKey(e>e.getLastName())).collect(Collectors.toList());

    public static <T> Predicate<T> distincyByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen =  ConcurrentHashMap.newKeySet();
        System.out.println("testttttttttttt");
        return t -> seen.add(keyExtractor.apply(t));
    }

这种方法似乎奏效了。不知道方法 1 和 2 有什么区别。我看到 distincyByKey 是一个静态方法,但它有什么区别?

【问题讨论】:

    标签: lambda java-8 functional-programming java-stream


    【解决方案1】:

    每次执行 lambda 表达式主体时,您的第一个 sn-p 都会创建一个新的 Set 实例,这意味着对于 Stream 的每个元素一次。因此,每次对Stream 的元素应用过滤器时,都会将该元素添加到不同的Set 实例中,因此无法过滤掉重复项。

    在您的第二个 sn-p 中,Set 实例在执行 distincyByKey() 时创建一次。当对 Stream 的每个元素评估过滤器时,执行的 lambda 主体只是 seen.add(keyExtractor.apply(t),并且始终针对同一个 Set 实例执行。

    您可以在不使用 static 方法的情况下通过在 Stream 管道之外创建 Set 实例或使用方法引用来实现相同的行为:

    Set<String> seen = ConcurrentHashMap.newKeySet();
    List<String> list1 = 
        list.parallelStream().filter(e-> seen.add(e)).collect(Collectors.toList());
    

    List<String> list1 = 
        list.parallelStream().filter(ConcurrentHashMap.newKeySet()::add).collect(Collectors.toList());
    

    编辑:我从代码中删除了getLastName() 方法调用,因为您的StreamStream&lt;String&gt;,而Strings 没有getLastName() 方法。我猜你混淆了一些代码 sn-ps。

    【讨论】:

    • 非常感谢。您能否告诉我它是否在 lambda 或流的功能中指定?
    • @user2296988 我不确定您究竟希望看到什么。重要的是要了解代码的每个语句何时执行。传递给filter() 的参数在创建Stream 时被评估一次。传递给filter()Predicatetest() 方法对Stream 的每个元素执行。一旦你弄清楚代码的哪一部分成为每个 sn-p 中 test() 方法的实现,你就会看到不同之处。
    • 哦,有道理。因此,通常 lambda 应该只有参数和返回语句(或 void)。如果它在 body 实现中有任何实例变量,它也将被视为一个参数。谢谢
    • 您能否解释一下是否有任何方法可以让您使用第二个成员变量。例如,我将一个类的变量传递给谓词。
    • @user2296988 我得看看实际的示例代码。您的评论还不够清楚。
    【解决方案2】:

    两者之间的区别完全在于您的 Set 对象。

    在第一个代码 sn-p 中,seen 由谓词创建。为每个元素调用谓词。这意味着每次调用谓词时,都会创建一个集合实例。第二种,集合由distincyByKey()创建,在创建流时只运行一次。

    distincyByKey 返回的谓词包含对该集合的引用,并且每次运行时,它都使用相同的实例。如果您要让第二个代码像第一个代码一样运行,该方法将如下所示:

    public static <T> Predicate<T> distincyByKey(Function<? super T, ?> keyExtractor) {
        return t -> {
            Set<Object> seen =  ConcurrentHashMap.newKeySet();
            return seen.add(keyExtractor.apply(t));
        }
    }
    

    这个版本会和你的第一个实现一样坏。

    【讨论】:

    • 哦,哇,从来不知道你可以这样写返回语句。好帖子!
    猜你喜欢
    • 1970-01-01
    • 2011-08-26
    • 2019-10-24
    • 1970-01-01
    • 1970-01-01
    • 2011-03-23
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    相关资源
    最近更新 更多