Java 并行流在不同的操作下工作正常吗?
我在读关于无国籍的书时,在以下方面遇到了这个问题: 如果 流操作的行为参数是有状态的。A. 有状态lambda(或其他实现适当 功能接口)的结果取决于 在执行流管道期间可能会发生更改 现在,如果我有一个字符串列表(Java 并行流在不同的操作下工作正常吗?,java,java-8,java-stream,Java,Java 8,Java Stream,我在读关于无国籍的书时,在以下方面遇到了这个问题: 如果 流操作的行为参数是有状态的。A. 有状态lambda(或其他实现适当 功能接口)的结果取决于 在执行流管道期间可能会发生更改 现在,如果我有一个字符串列表(strListsay),然后尝试通过以下方式使用并行流从中删除重复的字符串: List<String> resultOne = strList.parallelStream().distinct().collect(Collectors.toList()); List r
strList
say),然后尝试通过以下方式使用并行流从中删除重复的字符串:
List<String> resultOne = strList.parallelStream().distinct().collect(Collectors.toList());
List resultOne=strList.parallelStream().distinct().collect(Collectors.toList());
或者,如果我们希望不区分大小写:
List<String> result2 = strList.parallelStream().map(String::toLowerCase)
.distinct().collect(Collectors.toList());
List result2=strList.parallelStream().map(字符串::toLowerCase)
.distinct().collect(collector.toList());
这段代码会有任何问题吗?因为并行流会分割输入,并且在一个块中的distinct不一定意味着在整个输入中的distinct
编辑(以下答案的快速摘要)
distinct
是一种有状态操作,在有状态中间操作的情况下,并行流可能需要多次传递或大量缓冲开销。此外,如果元素的顺序不相关,则可以更有效地实现distinct
。
还根据:
对于有序流,不同元素的选择是稳定的(对于
重复的元素,在遭遇战中首先出现的元素
对于无序流,没有稳定性保证
这是我们制造的
但在有序流并行运行的情况下,distinct可能不稳定——这意味着在重复的情况下,它将保留任意元素,而不一定是distinct
中预期的第一个元素
从:
在内部,distinct()操作保留一个包含
以前见过的元素,但它埋在
操作,我们无法从应用程序代码获取它
因此,在并行流的情况下,它可能会消耗整个流或使用CHM(类似于ConcurrentHashMap.newKeySet()
)。对于有序的,最有可能使用的是LinkedHashSet
或类似的构造。不会有问题(问题是错误的结果),但正如注释所述
在并行管道中保持distinct()的稳定性相对昂贵
但是,如果性能值得关注,并且不是问题(即结果的元素顺序与它处理的集合不同),那么您需要遵循API的说明
删除BaseStream.unordered()的排序约束可能会
使中的distinct()执行效率显著提高
平行管道
我想为什么不为distinct
public static void main(String[] args) {
List<String> strList = Arrays.asList("cat", "nat", "hat", "tat", "heart", "fat", "bat", "lad", "crab", "snob");
List<String> words = new Vector<>();
int wordCount = 1_000_000; // no. of words in the list words
int avgIter = 10; // iterations to run to find average running time
//populate a list randomly with the strings in `strList`
for (int i = 0; i < wordCount; i++)
words.add(strList.get((int) Math.round(Math.random() * (strList.size() - 1))));
//find out average running times
long starttime, pod = 0, pud = 0, sod = 0;
for (int i = 0; i < avgIter; i++) {
starttime = System.currentTimeMillis();
List<String> parallelOrderedDistinct = words.parallelStream().distinct().collect(Collectors.toList());
pod += System.currentTimeMillis() - starttime;
starttime = System.currentTimeMillis();
List<String> parallelUnorderedDistinct =
words.parallelStream().unordered().distinct().collect(Collectors.toList());
pud += System.currentTimeMillis() - starttime;
starttime = System.currentTimeMillis();
List<String> sequentialOrderedDistinct = words.stream().distinct().collect(Collectors.toList());
sod += System.currentTimeMillis() - starttime;
}
System.out.println("Parallel ordered time in ms: " + pod / avgIter);
System.out.println("Parallel unordered time in ms: " + pud / avgIter);
System.out.println("Sequential implicitly ordered time in ms: " + sod / avgIter);
}
(二)
(三)
无序平行线的速度是两者的两倍
然后我把wordCount
增加到500000
,结果如下
(一)
(二)
(三)
然后转到10\u 000\u 000
(一)
(二)
(三)
大致指出(重点,矿山)的相关部分: 中间操作进一步分为无状态和 有状态操作。无状态操作,如筛选和映射, 处理新元素时,不保留以前看到的元素的任何状态 元素——每个元素都可以独立于操作进行处理 关于其他因素有状态操作,如distinct和sorted, 处理时可能会合并以前看到的元素的状态 新元素 有状态操作可能需要在执行之前处理整个输入 产生结果。例如,无法从中产生任何结果 对流进行排序,直到看到流的所有元素作为一个 结果,在并行计算下,一些管道包含有状态 中间操作可能需要多次传递数据,或者 需要缓冲重要数据。仅包含 无状态中间操作可以在一次过程中处理, 无论是顺序的还是并行的,具有最小的数据缓冲 如果您进一步阅读(订购部分): 流可能有也可能没有定义的遭遇顺序。是否 流的遭遇顺序取决于源和目标 中间业务某些流源(如列表或 数组)本质上是有序的,而其他数组(如HashSet) 不是。某些中间操作(如sorted())可能会强制执行 在其他无序流中遇到订单,其他人可能会 以无序方式呈现有序流,例如BaseStream.unordered()。 此外,一些终端操作可能会忽略遭遇顺序,例如 forEach() 对于并行流,有时可以放松排序约束 实现更高效的执行某些聚合操作,例如 筛选重复项(distinct())或分组的缩减 (Collectors.groupingBy())可以更高效地实现,如果 元素的顺序不相关。类似地,以下操作 与遭遇顺序有内在联系,例如limit(),可能需要 缓冲以确保正确排序,从而损害 平行性在流具有遭遇顺序的情况下,但是 用户并不特别关心这种遭遇顺序 使用unordered()对流进行反排序可以提高并行性 某些有状态或终端操作的性能。然而,大多数 河流管道,如上面的“区块重量总和”示例, 即使在以下情况下仍能有效地并行化
Parallel ordered time in ms: 52
Parallel unordered time in ms: 81
Sequential implicitly ordered time in ms: 35
Parallel ordered time in ms: 48
Parallel unordered time in ms: 83
Sequential implicitly ordered time in ms: 34
Parallel ordered time in ms: 36
Parallel unordered time in ms: 70
Sequential implicitly ordered time in ms: 32
Parallel ordered time in ms: 93
Parallel unordered time in ms: 363
Sequential implicitly ordered time in ms: 123
Parallel ordered time in ms: 100
Parallel unordered time in ms: 363
Sequential implicitly ordered time in ms: 124
Parallel ordered time in ms: 89
Parallel unordered time in ms: 365
Sequential implicitly ordered time in ms: 118
Parallel ordered time in ms: 148
Parallel unordered time in ms: 725
Sequential implicitly ordered time in ms: 218
Parallel ordered time in ms: 150
Parallel unordered time in ms: 749
Sequential implicitly ordered time in ms: 224
Parallel ordered time in ms: 143
Parallel unordered time in ms: 743
Sequential implicitly ordered time in ms: 222
List<String> result2 = strList.parallelStream()
.unordered()
.map(String::toLowerCase)
.distinct()
.collect(Collectors.toList());