Java 如何对流的某些部分执行缩减操作

Java 如何对流的某些部分执行缩减操作,java,java-stream,Java,Java Stream,我遇到了一个我认为可以使用流API处理的情况,但我无法找到合适的解决方案 情况如下:我有一个按标识符字段排序的元素流。有几个元素具有相同的标识符值,我需要根据其他字段的条件对它们进行重复数据消除。从概念上讲,它可以被看作是对流的多个块执行的reduce操作,从而生成相同类型的流 目前,我唯一的解决方案是根据公共标识符收集流,以获得类似于Map的内容,然后使用此映射的流应用我的重复数据消除规则并继续。问题(以及为什么我不使用这个解决方案)是collect是一个终端操作,在它之后重新流式处理意味着我

我遇到了一个我认为可以使用流API处理的情况,但我无法找到合适的解决方案

情况如下:我有一个按标识符字段排序的元素流。有几个元素具有相同的标识符值,我需要根据其他字段的条件对它们进行重复数据消除。从概念上讲,它可以被看作是对流的多个块执行的reduce操作,从而生成相同类型的流

目前,我唯一的解决方案是根据公共标识符收集流,以获得类似于
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