Java流 - 收集组合器

时间:2018-03-05 13:33:19

标签: java java-stream collectors

为什么以下代码:     

StringBuilder sb22 = IntStream
     .range(1, 101)
     .filter(x -> x > 50)
     .boxed()
     .parallel()
     .collect(// object that is used in accumulator to do accumulating on
              StringBuilder::new, 
              // use object from above and call append on it with each stream element as argument
              (sb, a) -> sb.append(":" + a),  
              // (executes only when using parallel!)
              (sb1, sb2) -> { 
                     System.out.println(Thread.currentThread().getId() + "  " + "sb1=" + sb1 + " AND " + "sb2=" + sb2); 
                     sb1.append("-"+sb2);
              });

产生这个结果:

------------------:51:52:53-:54:55:56-:57:58:59-:60:61:62-:63:64:65-:66:67:68-:69:70:71-:72:73-:74:75-:76:77:78-:79:80:81-:82:83:84-:85:86:87-:88:89:90-:91:92:93-:94:95:96-:97:98-:99:100

不应该从输出中排除第一部分(------------------)吗?

此外,我了解收集中的合并器可能会无序调用,因此可以改为:76:77:78-:79:80:81例如:63:64:65-:79:80:81 [51..100] _________________________________________________________________________________/\______________________________________________________________________ | | (empty) [51..100] ___________________________________/\__________________________________ ________________________________________/\______________________________________ | | | | (empty) (empty) [51..75] [76..100] ___________________/\______________ ___________________/\______________ ______________________/\________________ ______________________/\________________ | | | | | | | | (empty) (empty) (empty) (empty) [51..62] [63..75] [76..87] [88..100] _______/\______ ___________/\______ _______/\______ ___________/\______ ________/\_______ _____________/\_______ ________/\_______ _____________/\_______ | | | | | | | | | | | | | | | | (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) [51..56] [57..62] [63..68] [69..75] [76..81] [82..87] [88..93] [94..100] ___/\__ ___/\__ ___/\__ _______/\__ ___/\__ ___/\__ ___/\__ _______/\__ ___/\___ ___/\___ ___/\___ ________/\__ ___/\___ ___/\___ ___/\___ ________/\___ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) [51..53] [54..56] [57..59] [60..62] [63..65] [66..68] [69..71] [72..75] [76..78] [79..81] [82..84] [85..87] [88..90] [91..93] [94..96] [97..100] ___/\__ ___/\__ ___/\___ ____/\__ | | | | | | | | (empty) (empty) (empty) (empty) [72..73] [74..75] [97..98] [99..100]

更新(在@Holger回应之后)

这是使用他为此案例链接的代码生成的树:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.macbookpro.Main3Activity">


    <ScrollView
        android:id="@+id/scrollview1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"


            >


            <TextView
                android:id="@+id/title1"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_marginLeft="2dp"
                android:layout_marginStart="12dp"
                android:layout_marginTop="55dp"
                android:text="@string/title1"
                android:textAllCaps="true"
                android:textColor="@color/colorPrimaryDark"
                android:textSize="25sp" />

            <TextView
                android:id="@+id/textView4"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@+id/soutitle3"
                android:layout_marginEnd="12dp"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="12dp"
                android:layout_marginStart="15dp"
                android:layout_marginTop="10dp"
                android:text="@string/soutext3" />

            <TextView
                android:id="@+id/soutitle4"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_below="@+id/textView4"
                android:layout_marginLeft="2dp"
                android:layout_marginStart="8dp"
                android:layout_marginTop="15dp"
                android:text="@string/soutitle4"
                android:textColor="@color/colorPrimaryDark"
                android:textSize="18sp" />

            <TextView
                android:id="@+id/textView5"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@+id/soutitle4"
                android:layout_marginEnd="12dp"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="12dp"
                android:layout_marginStart="15dp"
                android:layout_marginTop="10dp"
                android:text="@string/soutext4" />
            <TextView
                android:id="@+id/soutitle5"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_below="@+id/textView5"
                android:layout_marginLeft="2dp"
                android:layout_marginStart="8dp"
                android:layout_marginTop="15dp"
                android:text="@string/soutitle5"
                android:textColor="@color/colorPrimaryDark"
                android:textSize="18sp" />

            <TextView
                android:id="@+id/textView6"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@+id/soutitle5"
                android:layout_marginEnd="12dp"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="12dp"
                android:layout_marginStart="15dp"
                android:layout_marginTop="10dp"
                android:text="@string/soutext5" />



        </RelativeLayout>


    </ScrollView>

    <com.google.android.gms.ads.AdView
        android:id="@+id/adView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        ads:adSize="SMART_BANNER"
        ads:adUnitId="ca-apppub3940256099942544/6300978111"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />


</android.support.constraint.ConstraintLayout>

3 个答案:

答案 0 :(得分:3)

工作负载拆分在任何处理之前发生,因此,Stream实现会将范围<span>拆分为要处理的子范围。此时,它不知道过滤器将完全删除前半部分,它无法在不评估谓词的情况下知道,并且应该已经并行发生,因此,在工作负载分割之后。

因此,每个子范围都以相同的方式处理,包括将结果收集到容器中,然后将这些容器组合起来,即使它们恰好是空的。规范并没有说当没有元素到达收集器时会跳过组合步骤,因此,你不应该期望这样。虽然理论上可以跟踪是否有任何元素到达收集器,但这种跟踪只能用于特定情况,甚至不清楚是否将容器与空容器组合(如添加空[1, 101]或附加空List}比此追踪更贵。

当然,如果它保留了语义,例如,没有什么能阻止你优化你的组合器。而不是StringBuilder,您可以使用(sb1, sb2) -> sb1.append(sb2)

您可以查看this Q&A, “Visualization of Java Stream parallelization”了解详情。

答案 1 :(得分:2)

看起来尝试优化导致创建一些不必要的StringBuilder来处理x < 51。由于过滤器,这些构建器从未累积过字符串,但即使它们是空的,它们仍然与其他构建器连接在一起。也许通过更智能的优化,可以消除部分工作。

关于你的第二个问题,如果你只想在连接期间交换订单,你会写sb2.append(sb1),虽然这会产生不可靠的结果,因为你以不同的顺序附加,这种不一致的行为会违背合同。

答案 2 :(得分:2)

你在sb1.append("-"+sb2)中破坏了关联性,这是由文档说明的。因此,在并行执行中,您获得的内容实际上是未知/不可预测的。

正确的combiner例如StringBuilder::append或lambda:

(left, right) -> left.append(right)

它们不会出现故障,它们会保留订单(无论订单是什么)。例如,如果您从HashSet(没有任何订单)流式传输,您将获得不同的结果。可能使用java-9和Set.of这个结果会因运行而异。