This question already has answers here:
Collect successive pairs from a stream

(20个答案)


4年前关闭。




在掌握Java 8流的过程中,以下练习使我停了下来。

给定IntStream.range(0, 6)。在下面产生字符串流:
"0, 1"
"1, 2"
"2, 3"
"3, 4"
"4, 5"

我想到了使用Collectors.collectAndThen将其传递到良好的旧列表或数组,然后循环构建字符串列表,如下所示:
List<String> strgs = new ArrayList<>();
String prev = String.valueOf(nums[0]);
for (int i = 1; i < nums.length; i++) {
    strgs.add(prev+", "+String.valueOf(nums[i]));
    prev = String.valueOf(nums[i]);
}

但是它不使用流的力量。我感到Venkat Subramaniam说:“之后我想洗个澡”。
我想知道如何应用功能技术,以便在编码后跳过淋浴!

另外,我想避免使用StreamEx或JavaRx之类的库,而是要坚持使用普通的Java 8 API。

编辑:
@Tunaki,感谢您指出我的问题中措辞不清楚。它是由Stream的两个连续元素组成的一对。更具体地说,像[1, 3, 5, 7, 9, ...]这样的Stream将是
"1, 3"
"3, 5"
"5, 7"
...

编辑2

向所有答案致敬后,尽管我的问题与Tunaki指出的另一个问题重复。我想在社区中讨论波希米亚的答案。尽管他的回答被某些人不满意,但它提出了一个严重的问题,即减少副作用的操作。我对社区的要求是针对此问题提供合理的反有效技术。因此,我想按以下方式重用波希米亚答案:

给定输入:nums = new int [] {1,3,5,7,9}

请考虑以下代码段:
List<CharSequence> stringList = new ArrayList<>();
IntBinaryOperator reductionWithSideEffect = (int left, int right) -> {
        stringList.add(new StringBuilder().append(left).append(", ").append(right));
        return right;
};
Arrays.stream(nums)
        .reduce(reductionWithSideEffect);
System.out.println(String.join(", ", stringList));

最佳答案

在我看来,解决此问题的最干净方法是编写自定义拆分器并在其上创建Stream。如果您不需要绝对最大的性能并且不关心并行处理(并行流可以工作,但是效率很低),这并不是很难。这样的事情会起作用:

public static <T, R> Stream<R> pairMap(BaseStream<T, ?> source,
        BiFunction<? super T, ? super T, ? extends R> mapper) {
    Spliterator<T> spltr = source.spliterator();
    long sourceSize = spltr.estimateSize();
    Spliterator<R> result = new Spliterators.AbstractSpliterator<R>(
            sourceSize > 0 && sourceSize < Long.MAX_VALUE ? sourceSize - 1 : sourceSize,
            spltr.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) {
        T prev;
        boolean started;

        @Override
        public boolean tryAdvance(Consumer<? super R> action) {
            if (!started) {
                if (!spltr.tryAdvance(t -> prev = t))
                    return false;
                started = true;
            }
            return spltr.tryAdvance(t -> action.accept(mapper.apply(prev, prev = t)));
        }
    };
    return StreamSupport.stream(result, source.isParallel()).onClose(source::close);
}

此处mapper是根据输入流的一对相邻元素创建新流的元素的函数。

用法示例:
pairMap(IntStream.range(0, 6), (a, b) -> a + ", " + b).forEach(System.out::println);

输出:
0, 1
1, 2
2, 3
3, 4
4, 5

10-07 19:48
查看更多