[英]How to log a message if a stream is empty within the Stream?
Given the following Java 8 Stream:给定以下 Java 8 Stream:
scheduleService.list().stream()
.filter(Schedule::getEnabled)
.filter(this::runnable)
.flatMap(s -> s.getJobs().stream())
// .doSomethingArbitrary(System.out.println("A single message. The total number of
// elements in the stream after filtering is " + this::count))
.forEach(this::invoke);
After the filtering has been applied to the stream and after the first terminal operation has been applied, I would like to either log a debug message if the stream is empty or if it isn't, call the invoke
method on each element in the stream. Is this possible?在将过滤应用于 stream 并应用第一个终端操作之后,如果 stream 为空,我想记录一条调试消息,或者如果不是,则对 stream 中的每个元素invoke
方法。 这可能吗?
You can create a custom Collector
(here called StreamInterceptor
), even though this does not really fit the purpose of a collector.您可以创建一个自定义Collector
(此处称为StreamInterceptor
),即使这并不真正符合收集器的目的。
Stream<T>
to a List<T>
将Stream<T>
转换为List<T>
Stream<T>
of the List<T>
返回List<T>
的新Stream<T>
T>Here I've just broken down your problem into the filtering of a simple string list and printing them to the console at the end.在这里,我刚刚将您的问题分解为过滤一个简单的字符串列表,并在最后将它们打印到控制台。
public static void main(String[] args) {
List<String> myList = List.of("first", "second", "third");
myList.stream()
.filter(string -> !string.equals("second"))
.collect(printCount())
.forEach(System.out::println);
}
/**
* Creates a StreamInterceptor, which will print the length of the stream
*/
private static <T> StreamInterceptor<T> printCount() {
Consumer<List<T>> listSizePrinter = list -> System.out.println("Stream has " + list.size() + " elements");
return new StreamInterceptor<>(listSizePrinter);
}
When initializing the StreamInterceptor
you can define a Consumer, that takes in the intermediate list constructed from the stream and performs some action on it.在初始化StreamInterceptor
时,您可以定义一个 Consumer,它接收从 stream 构造的中间列表并对其执行一些操作。 In your case it will just print the size of the list.在您的情况下,它只会打印列表的大小。
StreamInterceptor
class新的StreamInterceptor
classimport java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Stream;
class StreamInterceptor<T> implements Collector<T, List<T>, Stream<T>> {
private final Consumer<List<T>> listConsumer;
public StreamInterceptor(Consumer<List<T>> listConsumer) {
this.listConsumer = listConsumer;
}
@Override
public Supplier<List<T>> supplier() {
return ArrayList::new;
}
@Override
public BiConsumer<List<T>, T> accumulator() {
return List::add;
}
@Override
public BinaryOperator<List<T>> combiner() {
return (list1, list2) -> {
list1.addAll(list2);
return list1;
};
}
@Override
public Function<List<T>, Stream<T>> finisher() {
return list -> {
listConsumer.accept(list);
return list.stream();
};
}
@Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
}
You could always define a custom mapper that does the logging: 您始终可以定义执行日志记录的自定义映射器:
public static Stream<Job> loggingMapper(Service service) {
if( service.getJobs().isEmpty() ) {
System.out.println("Empty!"); // Or some other logging code
}
return service.getJobs().stream();
}
Then 然后
// same
.flatMap(SomeClass::loggingMapper)
In any case your mapper has to return a stream. 无论如何,您的映射器必须返回一个流。
You could wrap your Stream
into a custom method like the following 您可以将Stream
包装成自定义方法,如下所示
Stream<???> stream = scheduleService.list().stream()
.filter(Schedule::getEnabled)
.filter(this::runnable)
.flatMap(s -> s.getJobs().stream());
forEachOrElse(stream, this::invoke, () -> System.out.println("The stream was empty"));
With forEachOrElse
being 有了forEachOrElse
public <T> void forEachOrElse(Stream<T> inStream, Consumer<T> consumer, Runnable orElse) {
AtomicBoolean wasEmpty = new AtomicBoolean(true);
inStream.forEach(e -> {
wasEmpty.set(false);
consumer.accept(e);
});
if (wasEmpty.get())
orElse.run();
}
I can't test it right now but it should do its magic 我现在无法测试它,但它应该发挥它的魔力
This isn't really "nice" at all, but you could use peek to look into your stream and set an AtomicBoolean: 这根本不是“好”,但您可以使用peek来查看您的流并设置AtomicBoolean:
AtomicBoolean empty = new AtomicBoolean(true);
scheduleService.list().stream()
.filter(Schedule::getEnabled)
.filter(this::runnable)
.flatMap(s -> s.getJobs().stream())
.peek(s -> ab.set(false);)
.forEach(this::invoke);
if(empty.get()){
// is Empty
}
You can add a peek
before converting the list to stream
. 您可以在将列表转换为stream
之前添加peek
。
public static void main(String[] args) {
Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4, 5), Collections.emptyList())
.filter(x -> x.size() % 2 == 0)
.peek(s -> System.out.println(s.isEmpty()))
.flatMap(Collection::stream)
.forEach(System.out::println);
}
Output 产量
false
4
5
true
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.