Java Stream:有没有办法迭代一次取两个元素而不是一个? [英] Java Stream: is there a way to iterate taking two elements a time instead of one?

查看:1365
本文介绍了Java Stream:有没有办法迭代一次取两个元素而不是一个?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们有这个流

Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j");

我希望在地图中保存相邻字符串的对,其中第一个以呃。

and I want to save in a map the couples of adjacent strings in which the first one starts with "err".

我想到的是这样的事情

Map<String, String> map = new HashMap<>();

Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j")
.reduce((acc, next) -> {
    if (acc.startsWith("err"))
        map.put(acc,next);
    if (next.startsWith("err"))
        return next;
    else
        return "";
});

但我对此并不完全满意有两个主要原因

But I'm not totally satisfied with it for two main reasons


  1. 我误用 reduce 功能。在Stream API中,每个函数都有明确的,定义明确的目的: max 应该计算最大值,过滤器应该是要根据条件进行过滤, reduce 应该产生递增累积值,依此类推。

  2. 这样做会阻止我使用Streams强大的机制:如果我想将搜索限制为前两个结果怎么办?

  1. I'm "misusing" reduce function. In Stream API every function has its clear, well defined purpose: max is supposed to calcuate max value, filter is supposed to filter based on a condition, reduce is supposed to produce an incrementally accumulated value and so on.
  2. Doing that prevents me from using Streams powerful mechanisms: what if I wanted to limit my search to the first two results?

这里我使用 reduce 因为(据我所知)它是唯一可以让你比较几个值的函数,不知何故,可以回到类似于当前值和下一个值概念的东西。

Here I used reduce because (as far as I know) it's the only function that lets you compare couple of values that you can, somehow, lead back to something similar to "current value" and "next value" concepts.

是否有更直截了当的方式?是否允许您为每次迭代考虑多个值来迭代流?

Is there a more straightforward way? Something that allows you to iterate the stream considering more than one value for each iteration?

我是什么我想的是一种机制,给定当前元素,允许您为每次迭代定义要考虑的元素窗口。

What I'm thinking about is some mechanism that, given the current element, allows you to define a "window of elements" to consider, for each iteration.

类似

<R> Stream<R> mapMoreThanOne(
    int elementsBeforeCurrent,
    int elementsAfterCurrent,
    Function<List<? super T>, ? extends R> mapper);

而不是

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

这将是对当前API的强大升级。

That would be a powerful "upgrade" to current API.

我很欣赏人们提出解决方案的努力,但问题不在于算法本身。通过将流,索引,临时变量放在一起来存储以前的值,有不同的方法来实现我的目标...但我想知道Stream API中是否有一些方法是为处理除当前元素以外的元素而设计的没有打破流范式。这样的东西

I appreciate the effort of people proposing their solution, but the issue is not the algorithm per se. There are different ways to achieve my goal by putting together streams, indexes, temp variables to store previous values... but I was wondering if there was some method in Stream API that was designed for the task of dealing with elements other than the current without breaking the "stream paradigm". Something like this

List<String> list =
        Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j")
        .filterFunctionImWonderingIfExist(/*filters couples of elements*/)
        .limit(2)
        .collect(Collectors.toList());

鉴于答案,我认为除非使用StreamEx库,否则没有清晰快速的解决方案

Given the answers, I think there's no "clear and quick" solution, unless using StreamEx library

推荐答案

您可以为此任务构建自定义收集器。

You can build a custom Collector for this task.

Map<String, String> map = 
    Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j")
          .collect(MappingErrors.collector());

with:

private static final class MappingErrors {

    private Map<String, String> map = new HashMap<>();

    private String first, second;

    public void accept(String str) {
        first = second;
        second = str;
        if (first != null && first.startsWith("err")) {
            map.put(first, second);
        }
    }

    public MappingErrors combine(MappingErrors other) {
        throw new UnsupportedOperationException("Parallel Stream not supported");
    }

    public Map<String, String> finish() {
        return map;
    }

    public static Collector<String, ?, Map<String, String>> collector() {
        return Collector.of(MappingErrors::new, MappingErrors::accept, MappingErrors::combine, MappingErrors::finish);
    }

}

在这个收集器中,有两个运行元素被存管。每次接受 String 时,它们都会更新,如果第一个以err开头,则这两个元素是添加到地图。

In this collector, two running elements are kept. Each time a String is accepted, they are updated and if the first starts with "err", the two elements are added to a map.

另一个解决方案是使用 streamEx 库,它提供了一个 pairMap 将给定函数应用于此流的每个相邻元素对的方法。在下面的代码中,如果第一个元素以err开头,则操作返回一个String数组,该数组包含该对的第一个和第二个元素。 null 否则。然后过滤掉 null 元素,并将Stream收集到地图中。

Another solution is to use the StreamEx library which provides a pairMap method that applies a given function to the every adjacent pair of elements of this stream. In the following code, the operation returns a String array consisting of the first and second element of the pair if the first element starts with "err", null otherwise. null elements are then filtered out and the Stream is collected into a map.

Map<String, String> map = 
    StreamEx.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j")
            .pairMap((s1, s2) -> s1.startsWith("err") ? new String[] { s1, s2 } : null)
            .nonNull()
            .toMap(a -> a[0], a -> a[1]);

System.out.println(map);

这篇关于Java Stream:有没有办法迭代一次取两个元素而不是一个?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆