当前位置: 首页 > 知识库问答 >
问题:

遇到订单友好/不友好的终端操作与并行/顺序与有序/无序流

濮阳耀
2023-03-14

受这个问题的启发,我开始研究有序流与无序流、并行流与顺序流以及终端操作,它们考虑的是相遇顺序,而终端操作则不考虑相遇顺序。

在链接问题的一个答案中,显示了一个类似于此的代码:

List<Integer> ordered = Arrays.asList(
    1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4);
List<Integer> result = new CopyOnWriteArrayList<>();

ordered.parallelStream().forEach(result::add);

System.out.println(ordered);
System.out.println(result);

名单确实不同。无序列表甚至从一次运行更改到另一次运行,表明结果实际上是不确定的。

因此,我创建了另一个示例:

CopyOnWriteArrayList<Integer> result2 = ordered.parallelStream()
        .unordered()
        .collect(Collectors.toCollection(CopyOnWriteArrayList::new));

System.out.println(ordered);
System.out.println(result2);

我希望看到类似的结果,因为流既并行又无序(可能unordered()是冗余的,因为它已经并行了)。但是,结果列表是有序的,即它等于源列表。

所以我的问题是为什么要订购收集的列表?收集是否总是尊重相遇顺序,即使是并行的、无序的流?是特定的Collectors.to集合(...)收集器强制遭遇顺序?

共有3个答案

西门靖琪
2023-03-14

留档流#收藏已经提到:

当并行执行时,可以实例化、填充和合并多个中间结果,以保持可变数据结构的隔离。因此,即使在与非线程安全的数据结构(如ArrayList)并行执行时,并行缩减也不需要额外的同步。

这意味着流#collect做两件主要的事情:split

但是我在jdk-8中有一个特殊的例子,你可以获取不同的结果:)。当创建一个无序的流流由流#生成,然后你可以在收藏家#toList上获取不同的结果,例如:

Set<Set<Integer>> result = IntStream.range(0, 10).mapToObj(__ -> {
        return unordered().parallel().collect(toSet());
}).collect(toSet());

assert result.each.size() == 100000; // ok
//                   v--- surprised, it was pass 
assert result.size() > 1; 
Stream<Integer> unordered() {
    AtomicInteger counter = new AtomicInteger();
    return Stream.generate(counter::getAndIncrement).limit(10000);
}
秦宏盛
2023-03-14

在当前的实现中,我检查了java-8和java-9,对于非并发收集器(Collector.Characteristics.unordered),在collect阶段忽略无序标志。实现是允许这样做的,这在某种程度上是一个类似的问题。

在您链接的同一个问题中,我提供了一个示例,说明了findFirst实际上是如何从jdk-8更改为jdk-9的。

楮景明
2023-03-14

收集器。toCollection返回一个收集器,该收集器缺少收集器。特点。无序特征。指定收集器的另一个收集器。特点。无序的可能表现不同。

也就是说:“无序”意味着没有保证,不保证变化。如果库发现最容易将无序的集合按顺序处理,则允许它这样做,并且允许该行为在星期二或满月时一个版本一个版本地更改。

(还请注意,如果要使用并行流,Collectors.to集合不需要使用并发集合实现;toCollection(ArrayList::new)可以正常工作。这是因为收集器没有收集器。特点。(CONCURRENT特性,因此它使用了一种即使在并行流中也适用于非并发集合的集合策略。)(

如果您使用的是无序流,但收集器不是无序的,反之亦然,我怀疑您是否能从框架中得到任何保证。如果有一张桌子,它会说“给你。”

对于流。collect,我认为当前的实施有三种策略可供选择:

  • 顺序:启动一个累加器,将元素累加到累加器中(按遭遇顺序,因为你为什么要费事洗牌这些元素?只需按你得到它们的顺序接受它们),调用finisher
 类似资料:
  • 本页包含内容: 用以输入/输出的参数指针 作为数组使用的参数指针 用作字符串参数的指针 指针参数转换的安全性 Objective-C和C的API常常会需要用到指针。Swift中的数据类型都原生支持基于指针的Cocoa API,不仅如此,Swift会自动处理部分最常用的将指针作为参数传递的情况。这篇文章中,我们将着眼于在Swift中让C语言指针与变量、数组和字符串共同工作。 用以输入/输出的参数指针

  • 问题内容: 让我们简单地输入数字1..20: 现在,让我们创建一个并行流,结合以不同的方式和收集的结果: 过滤步骤在这里基本上不执行任何操作,但是给流引擎增加了更多难度:现在它不知道输出的确切大小,因此一些优化被关闭了。我得到以下结果: 结果完全正确,一切正常。在第一种情况下,我要求跳过前两个元素,然后以没有特定的顺序收集到列表。在第二种情况下,我要求跳过第一个元素,然后变成无序并跳过另一个元素(

  • 我有一个记录课程: 我创建了一个包含很多记录的大列表。只有第二个和第五个值,即i/10000和i,稍后分别由getter使用。 请注意,前10000条记录的类别2为0,接下来的10000条记录的类别1等,而值1按顺序为0-114999。 我创建了一个既并行又排序的流。 我有一个ForkJoinPool,它维护8个线程,这是我电脑上的内核数。 我使用这里描述的技巧将流处理任务提交给我自己的,而不是常

  • 问题内容: 是否有办法将JavaScript字符串传递给MySQL的NodeJSfriendly?我正在尝试将电子邮件地址传递给我的NodeJS服务器并查询到MySQL数据库。在执行常规文本(例如用户名)时可以正常工作,但电子邮件地址却不能。显然,使用转义不是正确的答案,因为这并不意味着要进行SQL插入。我假设我需要一些PHP函数。 问题答案: 事实证明,mysql_real_escape_str

  • 是否有任何保证在顺序和有序流上的操作是按遇到顺序处理的? 我是说,如果我有这样的代码: 是否可以保证它将按照生成范围的遇到顺序执行myFunction()调用? 我找到了Stream类的JavaDocs草案,它明确地说明了以下内容: 对于顺序流管道,如果管道源具有已定义的遇到顺序,则所有操作都按照管道源的遇到顺序执行。 但是它没有提到顺序流,这个例子是针对并行流的(我的理解是,顺序流和并行流都是正

  • 问题内容: 我有一个MySQL数据库,其中存储了每个用户的数据。 我想为每个用户添加一个朋友列表。我应该为数据库中的每个用户创建一个朋友表还是有更好的方法? 问题答案: 假设所有朋友也都在用户表中,则需要一个朋友表,该表定义了简单的一对多关系-将用户表链接回自身。所以 其中UserIDLink1和UserIDLink2都是Users表上的外键。 例如,如果我有三个用户 并且Joe和Jane是朋友,