Given a (long) string src
, generate substrings (up to) len
characters long on demand by iterating over src
and shifting the reading frame by shift
characters every time.
StreamSupport.stream
works for me, but is there a better (more idiomatic) way to do this, for example, by using a method like Stream.iterate
and providing a Predicate
and UnaryOperator
?
/**
* Streams strings of length up to len. Each streamed string is created from the string src by removing
* first "shift" characters and adding last "shift" characters. The shifting starts from 2nd string onward. It's
* like traversing the given string by a fixed length frame that shifts forward "shift" characters at a time.
*
* @param src the source string, e.g. "abcde"
* @param len max length of the string to be streamed, no string streamed from this method will exceed this length, e.g. 2
* @param shift integer shift-width to traverse src, e.g. 1
* @return a Stream of strings, e.g. the above will stream "ab", "bc", "cd", and "de"
*/
private static Stream<String> streamStrings(String src, int len, int shift) {
Iterator<String> traverser = new Iterator<>() {
private int i = 0, j = Math.min(len, src.length()); // substring(i, j) is what we need
@Override
public boolean hasNext() {
return i < j && j <= src.length();
}
@Override
public String next() {
String n = src.substring(i, j);
i += shift;
if (j < src.length())
j = Math.min(i + len, src.length());
else
j = i + len;
return n;
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(traverser, Spliterator.SIZED), false);
}
Sample Call and Output
streamStrings("abcdef", 3, 2).forEach(System.out::println);
//abc
//cde
//ef