'Perform actions with streaming api before/after foreach
Here's the non-stream code that I want to reproduce using only the streaming api if possible:
List<String> list = Arrays.asList("one", "two", "three", "four");
list.removeIf(s -> s.length() <= 3);
if (!list.isEmpty()) {
System.out.println("before");
for (String elem : list) {
System.out.println(elem);
}
System.out.println("after");
}
Of course simple iteration is easy with a stream:
List<String> list = Arrays.asList("one", "two", "three", "four");
list.stream()
.filter(s -> s.length() > 3)
.forEach(System.out::println);
But I need to add the "before" and "after" lines only if the stream is not empty after filtering. Is this possible in one statement or do I need to collect?
This hardly seems like a better solution...
List<String> list = Arrays.asList("one", "two", "three", "four");
List<String> filtered = list.stream()
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
if (!filtered.isEmpty()) {
System.out.println("before");
filtered.stream().forEach(System.out::println);
System.out.println("after");
}
Update: The examples above with Strings are too simple. I actually need to write a json array if the filtered list is not empty. I cannot write an empty array.
private void writeJsonArray(JsonGenerator g, List<String> list) {
list.removeIf(s -> s.length() <= 3);
if (!list.isEmpty()) {
g.writeArrayFieldStart("arr");
for (String elem : list) {
g.writeString(elem);
}
g.writeEndArray();
}
}
Solution 1:[1]
You can use anyMatch() and Collectors.joining() with prefix and suffix:
private static String NEW_LINE = System.lineSeparator();
public static void print(List<String> list) {
String str = list.stream().anyMatch(s -> s.length() > 3)
? list.stream().filter(s -> s.length() > 3)
.collect(Collectors.joining(NEW_LINE,
"before" + NEW_LINE, NEW_LINE + "after"))
: "";
System.out.println(str);
}
As an alternative approach, you can reduce the stream using StringBuilder as identity. Then, if the StringBuilder isn't empty, you can insert "before" and append "after". This approach performs only one terminal operation.
public static void print(List<String> list) {
StringBuilder sb = list.stream()
.filter(str -> str.length() > 3)
.reduce(new StringBuilder(),
(s, str) -> s.append(str).append(NEW_LINE),
StringBuilder::append);
if (!sb.isEmpty()) {
sb.insert(0, NEW_LINE).insert(0, "before")
.append("after");
}
System.out.println(sb.toString());
}
Test:
List<String> list = Arrays.asList("one", "two", "three", "four");
print(list);
Output:
before
three
four
after
Solution 2:[2]
I would use Stream.concat for this:
Stream.concat(
Stream.of("before"),
Stream.concat(
list.stream().filter(s -> s.length() > 3),
Stream.of("after")))
.forEach(System.out::println);
You might find it more readable split the logic into separate statements:
Stream<String> stream = list.stream().filter(s -> s.length() > 3);
stream = Stream.concat(Stream.of("before"), stream);
stream = Stream.concat(stream, Stream.of("after"));
stream.forEach(System.out::println);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | VGR |
