使用分隔符在流中交错元素

2022-09-04 08:15:01

有没有一种很好的方法可以使用Java流来交错具有相同类型分隔符的流中的元素?

// Expected result in is list: [1, 0, 2, 0, 3]
List<Integer> is = Stream.of(1, 2, 3).intersperse(0).collect(toList()); 

这类似于 Haskell 和其他函数式语言中的穿插函数。

我已经看到许多如何以类似方式连接字符串的示例,但没有找到任何通用列表的解决方案。


答案 1

你可以用 flatMap 来做,但是在最后一个元素之后会得到一个额外的分隔符:

List<Integer> is = IntStream.of(1, 2, 3)
                            .flatMap(i -> IntStream.of(i, 0))
                            .collect(toList());

这是另一种方法,没有尾随分隔符:

List<Integer> is = IntStream.of(1, 2, 3)
                            .flatMap(i -> IntStream.of(0, i))
                            .skip(1)
                            .collect(toList());

这次我们在每个原始元素之前添加分隔符,并删除前导分隔符。


答案 2

你可以用一个小的辅助方法做类似的事情:Collectors.joiningString

static <T> Collector<T,List<T>,List<T>> intersperse(T delim) {
    return Collector.of(ArrayList::new, (l,e)-> {
        if(!l.isEmpty()) l.add(delim);
        l.add(e);
    }, (l,l2)-> {
        if(!l.isEmpty()) l.add(delim);
        l.addAll(l2);
        return l;
    });

然后你可以使用它类似于:Collectors.joining(delimiter)

List<Integer> l=Stream.of(1, 2, 3).collect(intersperse(0));

生产

[1, 0, 2, 0, 3]

请注意,由于保护这些操作的方式,这是线程安全的。collect


如果您不想收集到列表中,但插入分隔符作为中间流操作,则可以通过以下方式执行此操作:

Integer delimiter=0;
Stream.of(1, 2, 3)/** or any other stream source */
      .map(Stream::of)
      .reduce((x,y)->Stream.concat(x, Stream.concat(Stream.of(delimiter), y)))
      .orElse(Stream.empty())
      /* arbitrary stream operations may follow */

推荐