Is there a nice way to use Java streams to interleave elements in a stream with a separator of the same type?
// Expected result in is list: [1, 0, 2, 0, 3]
List<Integer> is = Stream.of(1, 2, 3).intersperse(0).collect(toList());
This is similar to the intersperse
function in Haskell and other functional languages.
I have seen many examples of how to join strings in a similar way but have not found any solutions for general lists.
You can do it with flatMap, but you'll get an additional separator after the last element :
List<Integer> is = IntStream.of(1, 2, 3)
.flatMap(i -> IntStream.of(i, 0))
.collect(toList());
Here's another way, without the trailing separator :
List<Integer> is = IntStream.of(1, 2, 3)
.flatMap(i -> IntStream.of(0, i))
.skip(1)
.collect(toList());
This time we add the separator before each original element, and get rid of the leading separator.
You can do a similar thing which Collectors.joining
does with String
s with a little helper method:
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;
});
then you can use it similar to Collectors.joining(delimiter)
:
List<Integer> l=Stream.of(1, 2, 3).collect(intersperse(0));
produces
[1, 0, 2, 0, 3]
Note that this is thread safe due to the way collect
guards these operations.
If you don’t want to collect into a list but insert the delimiters as an intermediate stream operation you can do it this way:
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 */
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With