I have the following code that iterates over Java DateTimeFormatter
pattern combinations of "E".."EEEE"
and "M".."MMMM"
.
My question is, is there an idiomatic (or just 'more idiomatic') way to use Java Streams in this case?
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class DateTimeFormattingStackOverflow {
static LocalDateTime dateTime = LocalDateTime.now();
static Stream<String> substrings(String str) {
return IntStream.range(1, str.length() + 1)
.mapToObj(i -> str.substring(0, i));
}
static void printDateTime(String pattern) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
System.out.println(pattern + ", " + dtf.format(dateTime));
}
public static void main(String[] args) {
Stream<String> patterns = substrings("EEEE")
.flatMap(e -> substrings("MMMM").map(m -> e + " " + m))
.map(em -> em + " d");
patterns.forEach(DateTimeFormattingStackOverflow::printDateTime);
}
}
Output
E M d, Sat 7 1
E MM d, Sat 07 1
E MMM d, Sat Jul 1
E MMMM d, Sat July 1
EE M d, Sat 7 1
EE MM d, Sat 07 1
EE MMM d, Sat Jul 1
EE MMMM d, Sat July 1
...
Stream. concat() method creates a concatenated stream in which the elements are all the elements of the first stream followed by all the elements of the second stream. The resulting stream is ordered if both of the input streams are ordered, and parallel if either of the input streams is parallel. The calls to Stream.
Join stream of strings – examplejoining() method takes separator string as argument and join all the strings in the stream using using this separator. For example, we use comma as separator then this method will result into a comma-separated string. Program output.
Stream forEach(Consumer action) performs an action for each element of the stream. Stream forEach(Consumer action) is a terminal operation i.e, it may traverse the stream to produce a result or a side-effect.
You are using an IntStream
to drive the string. That's one way to do it, and here are two other ways:
static Stream<String> substrings(String str) {
return str.length() == 1 ? Stream.of(str) :
Stream.concat(Stream.of(str), substrings(str.substring(1)));
}
This creates a Stream
recursively, while the other way would be as follows:
static Stream<String> substrings2(String str) {
return Stream.iterate(str, s -> s.substring(1)).limit(str.length());
}
This applies the given function to the previous result. As it creates an infinite stream, you have to use limit
.
I have slightly modified your main
method, so that you avoid one map
operation:
substrings("EEEE")
.flatMap(e -> substrings("MMMM").map(m -> e + " " + m + " d"))
.forEach(DateTimeFormattingStackOverflow::printDateTime);
I really don't know if the ways above are more or less idiomatic than your way, but if you ask me, the most idiomatic way to do this task is with a nested loop:
String e = "EEEE";
String m = "MMMM";
for (int i = 0; i < e.length(); i++)
for (int j = 0; j < m.length(); j++)
printDateTime(e.substring(i) + " " + m.substring(j) + " d");
And this might be translated to Java 8 as follows:
IntStream.range(0, e.length()).boxed()
.flatMap(i -> IntStream.range(0, m.length())
.mapToObj(j -> e.substring(i) + " " + m.substring(j) + " d"))
.forEach(DateTimeFormattingStackOverflow::printDateTime);
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