簡體   English   中英

如果 Stream 中的 stream 為空,如何記錄消息?

[英]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 ),即使這並不真正符合收集器的目的。


自定義收集器將做什么?

  1. Stream<T>轉換為List<T>
  2. 調用 Consumer<List>,在您的情況下它將打印列表的長度。
  3. 返回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 class

import 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM