为什么以下代码:
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>
答案 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
这个结果会因运行而异。