I want to take a stream of strings and turn it into a stream of word pairs. eg:
I have: { "A", "Apple", "B", "Banana", "C", "Carrot" }
I want: { ("A", "Apple"), ("Apple", "B"), ("B", "Banana"), ("Banana", "C") }
.
This is nearly the same as Zipping, as outlined at Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip)
However, that produces:
{ (A, Apple), (B, Banana), (C, Carrot) }
The following code works, but is clearly the wrong way to do it (not thread safe etc etc):
static String buffered = null;
static void output(String s) {
String result = null;
if (buffered != null) {
result = buffered + "," + s;
} else {
result = null;
}
buffered = s;
System.out.println(result);
}
// *****
Stream<String> testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot");
testing.forEach(s -> {output(s);});
We can convert a String to an InputStream object by using the ByteArrayInputStream class. The ByteArrayInputStream is a subclass present in InputStream class.
We can use Stream collect() function to perform a mutable reduction operation and concatenate the list elements. The supplier function is returning a new StringBuilder object in every call. The accumulator function is appending the list string element to the StringBuilder instance.
More specifically, reduction stream operations allow us to produce one single result from a sequence of elements, by repeatedly applying a combining operation to the elements in the sequence.
If you:
Then you can create a method to group elements from a stream using Java 8 low-level stream builders StreamSupport
and Spliterator
:
class StreamUtils {
public static<T> Stream<List<T>> sliding(int size, Stream<T> stream) {
return sliding(size, 1, stream);
}
public static<T> Stream<List<T>> sliding(int size, int step, Stream<T> stream) {
Spliterator<T> spliterator = stream.spliterator();
long estimateSize;
if (!spliterator.hasCharacteristics(Spliterator.SIZED)) {
estimateSize = Long.MAX_VALUE;
} else if (size > spliterator.estimateSize()) {
estimateSize = 0;
} else {
estimateSize = (spliterator.estimateSize() - size) / step + 1;
}
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<List<T>>(estimateSize, spliterator.characteristics()) {
List<T> buffer = new ArrayList<>(size);
@Override
public boolean tryAdvance(Consumer<? super List<T>> consumer) {
while (buffer.size() < size && spliterator.tryAdvance(buffer::add)) {
// Nothing to do
}
if (buffer.size() == size) {
List<T> keep = new ArrayList<>(buffer.subList(step, size));
consumer.accept(buffer);
buffer = keep;
return true;
}
return false;
}
}, stream.isParallel());
}
}
Methods and parameters naming was inspired in their Scala counterparts.
Let's test it:
Stream<String> testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot");
System.out.println(StreamUtils.sliding(2, testing).collect(Collectors.toList()));
[[A, Apple], [Apple, B], [B, Banana], [Banana, C], [C, Carrot]]
What about not repeating elements:
Stream<String> testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot");
System.out.println(StreamUtils.sliding(2, 2, testing).collect(Collectors.toList()));
[[A, Apple], [B, Banana], [C, Carrot]]
And now with an infinite Stream
:
StreamUtils.sliding(5, Stream.iterate(0, n -> n + 1))
.limit(5)
.forEach(System.out::println);
[0, 1, 2, 3, 4]
[1, 2, 3, 4, 5]
[2, 3, 4, 5, 6]
[3, 4, 5, 6, 7]
[4, 5, 6, 7, 8]
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