【问题标题】:Considering list elements that are added after filtered stream creation考虑在过滤流创建后添加的列表元素
【发布时间】:2023-03-16 03:48:02
【问题描述】:

给定以下代码:

List<String> strList = new ArrayList<>(Arrays.asList("Java","Python","Php"));

Stream<String> jFilter = strList.stream().filter(str -> str.startsWith("J"));

strList.add("JavaScript"); // element added after filter creation
strList.add("JQuery"); // element added after filter creation

System.out.println(Arrays.toString(jFilter.toArray())); 

哪个输出:

[Java, JavaScript, JQuery]

为什么JavaScriptJQuery是在创建过滤流后添加的,却出现在过滤结果中?

【问题讨论】:

    标签: java java-8 java-stream


    【解决方案1】:

    简短回答

    您在此之后假设:

    Stream<String> jFilter = strStream.filter(str -> str.startsWith("J"));
    

    返回以“J”开头的新元素流,即仅返回Java。然而这不是的情况;

    流是惰性的,即它们不执行任何逻辑,除非终端操作另有说明。

    流管道的实际执行从toArray() 调用开始,由于列表在终端toArray() 操作开始之前被修改,结果将是[Java, JavaScript, JQuery]

    更长的答案

    这是documentation 的一部分,其中提到了这一点:

    对于表现良好的流源,可以先修改源 码头运营开始,这些修改将 反映在被覆盖的元素上。例如,考虑以下 代码:

     List<String> l = new ArrayList(Arrays.asList("one", "two"));
     Stream<String> sl = l.stream();
     l.add("three");
     String s = sl.collect(joining(" "));  
    

    首先创建一个包含两个字符串的列表:“one”;和“二”。然后创建一个流 从那个列表中。接下来,通过添加第三个字符串来修改列表: “三”。最后收集并加入流的元素 一起。由于列表在终端收集之前被修改 操作开始,结果将是一串“一二三”。 从 JDK 集合返回的所有流,以及大多数其他 JDK 类,以这种方式表现良好;

    【讨论】:

      【解决方案2】:

      直到声明

      System.out.println(Arrays.toString(jFilter.toArray()));
      

      运行,流不做任何事情。要遍历流并执行中间操作(本例中为 filter),需要终端操作(示例中为 toArray)。

      在这种情况下,您可以做的是,例如,在添加其他元素之前捕获列表的大小:

      int maxSize = strList.size();
      Stream<String> jFilter = strStream.limit(maxSize)
                                        .filter(str -> str.startsWith("J"));
      

      limit(maxSize) 不允许超过初始元素通过管道。

      【讨论】:

        【解决方案3】:

        这是因为流从未被评估过。您从未在该流上调用“终端操作”来执行它,因为它们是惰性

        查看您的代码和输出的修改。过滤实际上发生在您调用终端操作员时。

         public static void main(String []args){
                 List<String> strList = new ArrayList<>();
            strList.add("Java");
            strList.add("Python");
            strList.add("Php");
        
            Stream<String> strStream = strList.stream();
        
            Stream<String> jFilter = strStream.filter(str -> {
                System.out.println("Filtering" + str);
                return str.startsWith("J");
                });
        
         System.out.println("After Stream creation");
            strList.add("JavaScript"); // element added after filter creation
            strList.add("JQuery"); // element added after filter creation
        
            System.out.println(Arrays.toString(jFilter.toArray()));
        
             }
        

        输出:

        After Stream creation
        FilteringJava
        FilteringPython
        FilteringPhp
        FilteringJavaScript
        FilteringJQuery
        [Java, JavaScript, JQuery]
        

        【讨论】:

          【解决方案4】:

          正如官方文档https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html 中所解释的,流没有存储空间,因此更像是迭代器而不是集合,并且是惰性求值的。

          因此,在您调用终端操作 toArray() 之前,流不会真正发生任何事情

          【讨论】:

            【解决方案5】:

            @Hadi J 的评论,但应该按照规则回答。

            因为streams 是懒惰的,当你调用终端操作时它会执行。

            【讨论】:

              【解决方案6】:

              toArray 方法是终端操作,它适用于列表的全部内容。要获得可预测的结果,请勿将stream 保存到临时变量中,因为它会导致误导性结果。更好的代码是:

              String[] arr = strList.stream().filter(str -> str.startsWith("J")).toArray();
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2021-07-01
                • 2018-05-29
                • 2011-07-05
                • 1970-01-01
                • 2013-12-12
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多