Java 如何对流的某些部分执行缩减操作
我遇到了一个我认为可以使用流API处理的情况,但我无法找到合适的解决方案 情况如下:我有一个按标识符字段排序的元素流。有几个元素具有相同的标识符值,我需要根据其他字段的条件对它们进行重复数据消除。从概念上讲,它可以被看作是对流的多个块执行的reduce操作,从而生成相同类型的流 目前,我唯一的解决方案是根据公共标识符收集流,以获得类似于Java 如何对流的某些部分执行缩减操作,java,java-stream,Java,Java Stream,我遇到了一个我认为可以使用流API处理的情况,但我无法找到合适的解决方案 情况如下:我有一个按标识符字段排序的元素流。有几个元素具有相同的标识符值,我需要根据其他字段的条件对它们进行重复数据消除。从概念上讲,它可以被看作是对流的多个块执行的reduce操作,从而生成相同类型的流 目前,我唯一的解决方案是根据公共标识符收集流,以获得类似于Map的内容,然后使用此映射的流应用我的重复数据消除规则并继续。问题(以及为什么我不使用这个解决方案)是collect是一个终端操作,在它之后重新流式处理意味着我
Map
的内容,然后使用此映射的流应用我的重复数据消除规则并继续。问题(以及为什么我不使用这个解决方案)是collect
是一个终端操作,在它之后重新流式处理意味着我将在元素上迭代两次
更新
考虑以下类别:
public static class Item {
private final int _id;
private final double _price;
public Item(final int id, final double price) {
_id = id;
_price = price;
}
public int id() {
return _id;
}
public double price() {
return _price;
}
}
和以下流:
final Stream<Item> items = Stream.<Item>builder()
.add(new Item(1, 4))
.add(new Item(1, 6))
.add(new Item(1, 3))
.add(new Item(2, 5))
.add(new Item(2, 1))
.add(new Item(3, 5))
.build();
final Stream items=Stream.builder()
.添加(新项目(1、4))
.增加(新项目(1、6))
.添加(新项目(1、3))
.增加(新项目(2、5))
.添加(新项目(2,1))
.添加(新项目(3、5))
.build();
完成所需操作后,如果重复数据消除规则为“价格最高”,则流应仅包含项目(1,6)、项目(2,5)和项目(3,5)
如果我必须这样做,我可以在项目具有相同id时使用它们,将它们备份到临时集合中,并在遇到具有不同id的项目时消除此集合中的重复数据
如果我首先使用collect按id对项目进行分组,我将在移动到下一个操作之前立即使用所有数据,我需要避免这种情况。对于大多数此类情况,像
地图这样的临时存储是不可避免的。毕竟,地图的高效查找算法允许识别每个元素所属的组。而且,第一个组可能包含源流的第一个和最后一个元素,确定是否是这种情况的唯一方法是迭代整个源流。对于预排序数据的特殊情况,这可能不是真的,但是API没有为分组操作提供一种利用这一点的方法。如果它存在的话,它就不能很好地支持并行流
但是考虑允许你将组减少到最终结果的位置。如果是真正的减少,您可以使用,例如,作为下游收集器。这允许您将元素收集到
Map
中,而不是Map
,因此您不会收集到以后必须减少的列表中
对于任何类似的情况,如果您可以将后续操作描述为收集器
,那么当遇到组的第一个元素时,它的处理确实会立即开始。请注意,还有其他组合收集器
s,如和。Java 9还将添加过滤
和平面映射
,因此您可以以下游收集器的形式表达许多典型的流
操作。为方便起见,将映射步骤与后续缩减步骤相结合
只有在分组完全完成后,才能通过访问Map.values()
对组进行进一步处理。如果最终结果应该是集合
,则无需再次对其进行流式处理,因为现有的集合操作已经足够了,例如,如果需要列表
而不是不特定的集合
,则可以使用新的ArrayList(map.values())
如果您担心在调用者对最终流开始终端操作之前不应执行该操作,则可以使用如下操作:
public Stream<ResultType> stream() {
return StreamSupport.stream(() -> items.stream()
.collect(Collectors.groupingBy(classificationFunc,
Collectors.reducing(id, mappingFunc, reductionFunc)))
.values().spliterator(),
Spliterator.SIZED, false);
}
items.collapse((a, b) -> a.id() == b.id(), (a, b) -> a.price() < b.price() ? b : a)
publicstream(){
返回StreamSupport.stream(()->items.stream()
.collect(收集器).groupingBy(分类功能,
Collectors.reduction(id,mappingFunc,reductionFunc)))
.values().spliterator(),
拆分器(大小,错误);
}
对于大多数此类情况,像地图一样的临时存储是不可避免的。毕竟,地图的高效查找算法允许识别每个元素所属的组。而且,第一个组可能包含源流的第一个和最后一个元素,确定是否是这种情况的唯一方法是迭代整个源流。对于预排序数据的特殊情况,这可能不是真的,但是API没有为分组操作提供一种利用这一点的方法。如果它存在的话,它就不能很好地支持并行流
但是考虑允许你将组减少到最终结果的位置。如果是真正的减少,您可以使用,例如,作为下游收集器。这允许您将元素收集到
Map
中,而不是Map
,因此您不会收集到以后必须减少的列表中
对于任何类似的情况,如果您可以将后续操作描述为收集器
,那么当遇到组的第一个元素时,它的处理确实会立即开始。请注意,还有其他组合收集器
s,如和。Java 9还将添加过滤
和平面映射
,因此您可以以下游收集器的形式表达许多典型的流
操作。为方便起见,将映射步骤与后续缩减步骤相结合
只有在分组完全完成后,才能通过访问Map.values()
对组进行进一步处理。如果最终的结果应该是一个集合
,那么就不必像现有的c