java - 如果流过滤条件不返回任何结果,则返回单个元素的列表

标签 java java-stream

我目前正在过滤一个流,但如果过滤器没有返回任何匹配项,我想返回一个默认值。这是在一系列额外的流中,所以我使用它来避免链在一个步骤没有任何结果时停止。

目前我通过将过滤器的结果收集到列表中来捏造它,如果列表为空,则创建我的新默认列表并将其作为流返回。如果列表不为空,则将结果转换回流以将其传回。

什么是更面向流的方式来实现这一目标而无需转到列表并返回到流?

最佳答案

避免收集整个流、避免丢失原始 Stream 特性并保持(大部分)优化的最佳解决方案是实现自定义 Spliterator在原始流的 Spliterator 为空的情况下处理默认值:

public static <E> Stream<E> defaultIfEmpty(Stream<E> source, Supplier<? extends E> other) {
    final boolean parallel = source.isParallel();
    final Spliterator<E> originalSpliterator = source.spliterator();

    // little optimization for streams of known size
    final long size = originalSpliterator.getExactSizeIfKnown();
    if (size == 0) {
        // source already reports that it is empty
        final Stream<E> defaultStream = Stream.of(other.get());
        if (parallel) {
            return defaultStream.parallel();
        } else {
            return defaultStream;
        }
    }

    final Spliterator<E> spliterator;
    if (size > 0) {
        // source already reports that it is non-empty
        spliterator = originalSpliterator;
    } else {
        // negative means unknown, so wrap the source
        spliterator = wrap(originalSpliterator, other);
    }
    return StreamSupport.stream(spliterator, parallel);
}

private static <E> Spliterator<E> wrap(final Spliterator<E> spliterator, final Supplier<? extends E> other) {
    return new Spliterator<E>() {
        boolean useOther = true;
        @Override
        public boolean tryAdvance(final Consumer<? super E> action) {
            boolean couldAdvance = spliterator.tryAdvance(action);
            if (!couldAdvance && useOther) {
                useOther = false;
                action.accept(other.get());
                return true;
            }
            useOther = false;
            return couldAdvance;
        }

        @Override
        public Spliterator<E> trySplit() {
            if (!useOther) {
                // we know the original spliterator was not empty, we will thus never need the default
                return spliterator.trySplit();
            }
            Stream.Builder<E> builder = Stream.builder();
            if (spliterator.tryAdvance(builder)) {
                useOther = false;
                return builder.build().spliterator();
            } else {
                // spliterator is empty, but we will handle it in tryAdvance
                return null;
            }
        }

        @Override
        public long estimateSize() {
            long estimate = spliterator.estimateSize();
            if (estimate == 0 && useOther) {
                estimate = 1;
            }
            return estimate;
        }

        @Override
        public int characteristics() {
            // we don't actually change any characteristic of the original spliterator
            return spliterator.characteristics();
        }
    };
}

使用示例:

System.out.println(defaultIfEmpty(Stream.empty(), () -> 42).collect(toList()));
System.out.println(defaultIfEmpty(Stream.of(1, 2, 3), () -> 42).collect(toList()));
System.out.println(defaultIfEmpty(Stream.iterate(1, i -> i+1).parallel().filter(i -> i%3 == 0).limit(10), () -> 42).collect(toList()));
System.out.println(defaultIfEmpty(Stream.iterate(1, i -> i+1).parallel().limit(3).filter(i -> i%4 == 0), () -> 42).collect(toList()));

输出:

[42]
[1, 2, 3]
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
[42]

关于java - 如果流过滤条件不返回任何结果,则返回单个元素的列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50316078/

相关文章:

java - 倾城:无法获得 ru.yandex.qat ools.allure 的报告:allure-maven-plugin

java - 如何使用流 api 重写它

Java 8 流 : Combine properties of two objects which are in ArrayLists into an ArrayList of a third object type

java-8 - 使用流将列表转换为映射

java流: custom collector

java - 使用 Streams 进行嵌套循环以实现最佳性能

java - Gradle中的发布jar

java - 我的 Android 应用程序将默认语言更改为英语

java - shiro 中的 SslFilter 不会重定向回 http

java - Servlet 链接 - 简单示例