用分隔符交错流中的元素

Lii*_*Lii 8 java java-8 java-stream

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

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

这类似于intersperseHaskell和其他函数语言中的函数.

我已经看到很多关于如何以类似方式连接字符串但没有找到任何常规列表解决方案的示例.

Era*_*ran 8

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

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

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

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

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

  • @Lii添加了另一种方法,没有尾随分隔符且没有限制. (2认同)
  • @Eran 不确定您在发布时使用的编译器,但最新的 jdk-8/jdk-9 不起作用。缺少一个“盒装”。 (2认同)

Hol*_*ger 6

你可以用一个小帮助方法做一个Collectors.joiningStrings 相似的事情:

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;
    });
Run Code Online (Sandbox Code Playgroud)

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

List<Integer> l=Stream.of(1, 2, 3).collect(intersperse(0));
Run Code Online (Sandbox Code Playgroud)

产生

[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 */
Run Code Online (Sandbox Code Playgroud)