Java流:有没有一种方法可以一次迭代两个元素而不是一个?

Java流:有没有一种方法可以一次迭代两个元素而不是一个?,java,java-8,java-stream,Java,Java 8,Java Stream,假设我们有这条小溪 Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j"); 我想在一个映射中保存一对相邻的字符串,其中第一个字符串以“err”开头 我想到的是这样的 Map<String, String> map = new HashMap<>(); Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f

假设我们有这条小溪

Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j");
我想在一个映射中保存一对相邻的字符串,其中第一个字符串以“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 "";
});
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());
而不是

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

流映射(函数您可以编写自定义收集器,或者使用更简单的方法在列表索引上进行流式处理:

Map<String, String> result = IntStream.range(0, data.size() - 1)
        .filter(i -> data.get(i).startsWith("err"))
        .boxed()
        .collect(toMap(data::get, i -> data.get(i+1)));
以下是收集器的源代码。它对并行程序很友好,在其他情况下可能会派上用场:

public static <T, V, A, R> Collector<T, ?, R> pairing(BiPredicate<T, T> filter, BiFunction<T, T, V> map, Collector<? super V, A, R> downstream) {

    class Pairing {
        T left, right;
        A middle = downstream.supplier().get();
        boolean empty = true;

        void add(T t) {
            if (empty) {
                left = t;
                empty = false;
            } else if (filter.test(right, t)) {
                downstream.accumulator().accept(middle, map.apply(right, t));
            }
            right = t;
        }

        Pairing combine(Pairing other) {
            if (!other.empty) {
                this.add(other.left);
                this.middle = downstream.combiner().apply(this.middle, other.middle);
                this.right = other.right;
            }
            return this;
        }

        R finish() {
            return downstream.finisher().apply(middle);
        }
    }

    return Collector.of(Pairing::new, Pairing::add, Pairing::combine, Pairing::finish);
}
公共静态收集器配对(双预测过滤器、双功能映射、收集器您可以为此任务构建自定义

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

如果您的输入位于随机访问列表中,事情会变得更容易。通过这种方式,您可以使用良好的旧
列表。子列表
方法如下:

List<String> list = Arrays.asList("a", "b", "err1", "c", "d", "err2", "e", 
     "f", "g", "h", "err3", "i", "j");

Map<String, String> map = IntStream.range(0, list.size()-1)
    .mapToObj(i -> list.subList(i, i+2))
    .filter(l -> l.get(0).startsWith("err"))
    .collect(Collectors.toMap(l -> l.get(0), l -> l.get(1)));
List List=Arrays.asList(“a”、“b”、“err1”、“c”、“d”、“err2”、“e”,
“f”、“g”、“h”、“err3”、“i”、“j”);
Map Map=IntStream.range(0,list.size()-1)
.mapToObj(i->list.subList(i,i+2))
.filter(l->l.get(0).startsWith(“err”))
.collect(Collectors.toMap(l->l.get(0),l->l.get(1));
同样的事情也可以用前面提到的StreamEx库(由我编写)以稍微短一点的方式完成:

List<String> list = Arrays.asList("a", "b", "err1", "c", "d", "err2", "e", 
     "f", "g", "h", "err3", "i", "j");

Map<String, String> map = StreamEx.ofSubLists(list, 2, 1)
    .mapToEntry(l -> l.get(0), l -> l.get(1))
    .filterKeys(key -> key.startsWith("err"))
    .toMap();
List List=Arrays.asList(“a”、“b”、“err1”、“c”、“d”、“err2”、“e”,
“f”、“g”、“h”、“err3”、“i”、“j”);
Map Map=StreamEx.发布者(列表,2,1)
.mapToEntry(l->l.get(0),l->l.get(1))
.filterKeys(key->key.startsWith(“err”))
.toMap();

不过,如果您不希望第三方依赖,那么糟糕的流API解决方案看起来也不错。

下面是一个使用现成收集器的简单单行程序:

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

Map<String, String> map = Arrays.stream(stream
        .collect(Collectors.joining(",")).split(",(?=(([^,]*,){2})*[^,]*$)"))
    .filter(s -> s.startsWith("err"))
    .map(s -> s.split(","))
    .collect(Collectors.toMap(a -> a[0], a -> a[1]));
Stream-Stream=Stream.of(“a”、“b”、“err1”、“c”、“d”、“err2”、“e”、“f”、“g”、“h”、“err3”、“i”、“j”);
Map Map=Arrays.stream(stream
.collect(收集器.连接(“,”).split(“,(?=([^,]*,){2})*[^,]*$)”)
.filter(s->s.startsWith(“err”))
.map(s->s.split(“,”)
.collect(Collectors.toMap(a->a[0],a->a[1]);

这里的“诀窍”是首先将所有术语连接到一个字符串中,然后将其拆分为成对的字符串,例如
“a,b”
“err1,c”
,等等。一旦有了成对的流,处理就很简单了。

另一种方法是将
Collector.of
List
作为收集成对的结构。 首先收集到
列表

List collect=Stream.of(“a”、“b”、“err1”、“c”、“d”、“err2”、“e”、“f”、“g”、“h”、“err3”、“i”、“j”)
.收集(
收藏(
LinkedList::新建,
(a、b)->{
如果(b.startsWith(“err”))
a、 增加(新的阵列列表(列表(b));
如果(!a.isEmpty()&&a.getLast().size()==1),则为else
a、 getLast().add(b);
},
(a,b)->{抛出新的UnsupportedOperationException();}
)
);
然后可以将其转换为映射

Map-toMap=collect.stream().filter(l->l.size()==2)
.collect(collector.toMap)(
e->e.get(0),
e->e.get(1))
);
或使用
采集器的多功能一体机。采集器然后

Map toMap=Stream.of(“a”、“b”、“err1”、“c”、“d”、“err2”、“e”、“f”、“g”、“h”、“err3”、“i”、“j”)
收集,收集收集,然后收集(
收藏(
LinkedList::新建,
(a、b)->{
如果(b.startsWith(“err”))
a、 增加(新的阵列列表(列表(b));
如果(!a.isEmpty()&&a.getLast().size()==1),则为else
a、 getLast().add(b);
},
(a,b)->{抛出新的UnsupportedOperationException();}
),(x)->x.stream().filter(l->l.size()==2)
.collect(collector.toMap)(
e->e.get(0),
e->e.get(1))
)
));

看起来你需要一个收藏品,我想了想,但是我找不到一个(干净漂亮的)解决方案嗯,在最初的问题中,源流是一个日志文件,因此您实际上没有访问索引的权限。因此,没有更直接的方法。+1对于StreamEx库,非常有趣的项目!
PairMap
完美地解决了我的问题needs@LuigiCortese我不这么认为。那很不幸。Scala在Strea上有一个很好的方法M在这里是合适的:它把元素固定在固定大小的块中。我听说过。IMHO这个中间有一个很大的深孔。它可以更复杂,更容易。understandable@LuigiCortese使用
pairMap
解决方案,您可以在
.filter(Objects::nonNull)之后添加
.limit(2*n)
这样它就限制了n对情侣。还有为什么
.limit(2*n)
?似乎
.limit(n)
是@LuigiCortese想要的。我喜欢StreamEx背后的想法,我希望很快能推出一些有趣的PR=)@LuigiCortese,请注意,我并不急于接受每一个增强。每一个增强都有许多优点和缺点需要评估。例如,我可能会拒绝任何显示出较差并行性能的新功能。可能最好先在坏主意中讨论您的想法。这很容易出错。如果有
在其中一个输入字符串中,它将执行意外的操作。此外……这个疯狂的正则表达式是关于什么的?:-)
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);
List<String> list = Arrays.asList("a", "b", "err1", "c", "d", "err2", "e", 
     "f", "g", "h", "err3", "i", "j");

Map<String, String> map = IntStream.range(0, list.size()-1)
    .mapToObj(i -> list.subList(i, i+2))
    .filter(l -> l.get(0).startsWith("err"))
    .collect(Collectors.toMap(l -> l.get(0), l -> l.get(1)));
List<String> list = Arrays.asList("a", "b", "err1", "c", "d", "err2", "e", 
     "f", "g", "h", "err3", "i", "j");

Map<String, String> map = StreamEx.ofSubLists(list, 2, 1)
    .mapToEntry(l -> l.get(0), l -> l.get(1))
    .filterKeys(key -> key.startsWith("err"))
    .toMap();
Stream<String> stream = Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j");

Map<String, String> map = Arrays.stream(stream
        .collect(Collectors.joining(",")).split(",(?=(([^,]*,){2})*[^,]*$)"))
    .filter(s -> s.startsWith("err"))
    .map(s -> s.split(","))
    .collect(Collectors.toMap(a -> a[0], a -> a[1]));