[英]How to log a message if a stream is empty within the 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);
在将过滤应用于 stream 并应用第一个终端操作之后,如果 stream 为空,我想记录一条调试消息,或者如果不是,则对 stream 中的每个元素invoke
方法。 这可能吗?
您可以创建一个自定义Collector
(此处称为StreamInterceptor
),即使这并不真正符合收集器的目的。
Stream<T>
转换为List<T>
List<T>
的新Stream<T>
T>在这里,我刚刚将您的问题分解为过滤一个简单的字符串列表,并在最后将它们打印到控制台。
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);
}
在初始化StreamInterceptor
时,您可以定义一个 Consumer,它接收从 stream 构造的中间列表并对其执行一些操作。 在您的情况下,它只会打印列表的大小。
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();
}
}
您始终可以定义执行日志记录的自定义映射器:
public static Stream<Job> loggingMapper(Service service) {
if( service.getJobs().isEmpty() ) {
System.out.println("Empty!"); // Or some other logging code
}
return service.getJobs().stream();
}
然后
// same
.flatMap(SomeClass::loggingMapper)
无论如何,您的映射器必须返回一个流。
您可以将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"));
有了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();
}
我现在无法测试它,但它应该发挥它的魔力
这根本不是“好”,但您可以使用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
}
您可以在将列表转换为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);
}
产量
false
4
5
true
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.