Java 8 Collector UNORDERED 特征是什么意思?
What does the Java 8 Collector UNORDERED characteristic mean?
在官方文档中你可以看到:
UNORDERED
Indicates that the collection operation does not commit to
preserving the encounter order of input elements.
如果没有任何示例,这不是很有帮助。
我的问题是,UNORDERED
特征到底是什么意思?我应该将它与像 min 或 sum 这样的归约收集器一起使用,还是仅适用于集合收集器?
在 OpenJDK 中,减少操作(min、sum、avg)似乎具有空特征。我预计至少可以找到 CONCURRENT
和 UNORDERED
.
UNORDERED
本质上意味着收集器既是关联的(规范要求的)又是可交换的(不是必需的)。
关联性允许将计算拆分为子部分,然后将它们组合成完整结果,但要求组合步骤严格排序。从 docs:
检查这个片段
A a2 = supplier.get();
accumulator.accept(a2, t1);
A a3 = supplier.get();
accumulator.accept(a3, t2);
R r2 = finisher.apply(combiner.apply(a2, a3)); // result with splitting
在最后一步combiner.apply(a2, a3)
中,参数必须完全按照这个顺序出现,这意味着整个计算管道必须跟踪并最终遵守顺序。
换句话说,我们通过递归分裂得到的树必须是有序的。
另一方面,如果组合操作是可交换的,我们可以将任何子部分与任何其他部分组合,没有特定的顺序,并且总是获得相同的结果。显然,这会在 space 和时间维度上带来许多优化机会。
需要注意的是 JDK 中有 UNORDERED
个收集器不保证交换性。主要类别是 "higher-order" 收集器,它们与其他下游收集器组成,但它们不强制执行 UNORDERED
属性。
内部 Collector.Characteristics
class 本身的描述相当简洁,但如果您花几秒钟探索上下文,您会注意到包含 Collector 的界面提供了额外的信息
For collectors that do not have the UNORDERED characteristic, two accumulated results a1 and a2 are equivalent if finisher.apply(a1).equals(finisher.apply(a2)). For unordered collectors, equivalence is relaxed to allow for non-equality related to differences in order. (For example, an unordered collector that accumulated elements to a List would consider two lists equivalent if they contained the same elements, ignoring order.)
In OpenJDK looks like reducing operations (min, sum, avg) have empty characteristics, I expected to find there at least CONCURRENT and UNORDERED.
至少对于双精度求和和平均值肯定是有序的而不是并发的,因为求和逻辑使用子结果合并,而不是线程安全的累加器。
在没有特殊请求的情况下,流操作必须表现得好像元素是按照源的遇到顺序处理的。对于某些操作——例如使用关联操作进行归约——可以遵守此约束并仍然获得高效的并行执行。但是,对于其他人来说,这种限制非常有限。而且,对于某些问题,此约束对用户没有意义。考虑以下流管道:
people.stream()
.collect(groupingBy(Person::getLastName,
mapping(Person::getFirstName));
与 "Smith" 关联的名字列表按照它们在初始流中出现的顺序出现在地图中是否重要?对于一些问题,是的,对于一些不是——我们不希望流库为我们猜测。无序收集器表示可以按照与 Smith 姓氏在输入源中出现的顺序不一致的顺序将名字插入列表。通过放宽此约束,有时(并非总是),流库可以提供更高效的执行。
例如,如果你不关心这个顺序保存,你可以执行它:
people.parallelStream()
.collect(groupingByConcurrent(Person::getLastName,
mapping(Person::getFirstName));
并发收集器是无序的,这允许优化共享底层 ConcurrentMap
,而不是 O(log n)
映射合并步骤。放宽排序约束会带来真正的算法优势——但我们不能假设约束无关紧要,我们需要用户告诉我们这一点。使用 UNORDERED
收集器是告诉流库这些优化是公平游戏的一种方式。
在官方文档中你可以看到:
UNORDERED
Indicates that the collection operation does not commit to preserving the encounter order of input elements.
如果没有任何示例,这不是很有帮助。
我的问题是,UNORDERED
特征到底是什么意思?我应该将它与像 min 或 sum 这样的归约收集器一起使用,还是仅适用于集合收集器?
在 OpenJDK 中,减少操作(min、sum、avg)似乎具有空特征。我预计至少可以找到 CONCURRENT
和 UNORDERED
.
UNORDERED
本质上意味着收集器既是关联的(规范要求的)又是可交换的(不是必需的)。
关联性允许将计算拆分为子部分,然后将它们组合成完整结果,但要求组合步骤严格排序。从 docs:
检查这个片段 A a2 = supplier.get();
accumulator.accept(a2, t1);
A a3 = supplier.get();
accumulator.accept(a3, t2);
R r2 = finisher.apply(combiner.apply(a2, a3)); // result with splitting
在最后一步combiner.apply(a2, a3)
中,参数必须完全按照这个顺序出现,这意味着整个计算管道必须跟踪并最终遵守顺序。
换句话说,我们通过递归分裂得到的树必须是有序的。
另一方面,如果组合操作是可交换的,我们可以将任何子部分与任何其他部分组合,没有特定的顺序,并且总是获得相同的结果。显然,这会在 space 和时间维度上带来许多优化机会。
需要注意的是 JDK 中有 UNORDERED
个收集器不保证交换性。主要类别是 "higher-order" 收集器,它们与其他下游收集器组成,但它们不强制执行 UNORDERED
属性。
内部 Collector.Characteristics
class 本身的描述相当简洁,但如果您花几秒钟探索上下文,您会注意到包含 Collector 的界面提供了额外的信息
For collectors that do not have the UNORDERED characteristic, two accumulated results a1 and a2 are equivalent if finisher.apply(a1).equals(finisher.apply(a2)). For unordered collectors, equivalence is relaxed to allow for non-equality related to differences in order. (For example, an unordered collector that accumulated elements to a List would consider two lists equivalent if they contained the same elements, ignoring order.)
In OpenJDK looks like reducing operations (min, sum, avg) have empty characteristics, I expected to find there at least CONCURRENT and UNORDERED.
至少对于双精度求和和平均值肯定是有序的而不是并发的,因为求和逻辑使用子结果合并,而不是线程安全的累加器。
在没有特殊请求的情况下,流操作必须表现得好像元素是按照源的遇到顺序处理的。对于某些操作——例如使用关联操作进行归约——可以遵守此约束并仍然获得高效的并行执行。但是,对于其他人来说,这种限制非常有限。而且,对于某些问题,此约束对用户没有意义。考虑以下流管道:
people.stream()
.collect(groupingBy(Person::getLastName,
mapping(Person::getFirstName));
与 "Smith" 关联的名字列表按照它们在初始流中出现的顺序出现在地图中是否重要?对于一些问题,是的,对于一些不是——我们不希望流库为我们猜测。无序收集器表示可以按照与 Smith 姓氏在输入源中出现的顺序不一致的顺序将名字插入列表。通过放宽此约束,有时(并非总是),流库可以提供更高效的执行。
例如,如果你不关心这个顺序保存,你可以执行它:
people.parallelStream()
.collect(groupingByConcurrent(Person::getLastName,
mapping(Person::getFirstName));
并发收集器是无序的,这允许优化共享底层 ConcurrentMap
,而不是 O(log n)
映射合并步骤。放宽排序约束会带来真正的算法优势——但我们不能假设约束无关紧要,我们需要用户告诉我们这一点。使用 UNORDERED
收集器是告诉流库这些优化是公平游戏的一种方式。