当前位置: 首页 > 知识库问答 >
问题:

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

殳飞扬
2023-03-14

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

目前,我正在通过将筛选结果收集到一个列表中来伪造它,如果列表为空,请创建新的默认列表并将其作为流返回。如果列表不为空,则将结果转换回流以将其传递回。

有什么更流的方式来实现这一点,而不需要去列表和返回流?

共有2个答案

葛成济
2023-03-14

据我所知,您需要一个类似于C#中DefaultIfEmpty的方法。不幸的是,流API没有这样的方法,但幸运的是,有人已经实现了这种类型的东西。

从@Stuart Marks answer获取defaultIfEmpty方法,用例非常简单。

static <T> Stream<T> defaultIfEmpty(Stream<T> stream, Supplier<T> supplier) {
    Iterator<T> iterator = stream.iterator();
    if (iterator.hasNext()) {
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
    } else {
        return Stream.of(supplier.get());
    }
}

例如,sakes,假设您有一个整数列表:

List<Integer> integerList = new ArrayList<>(Arrays.asList(1,3,5,7,9)); 

如果上述列表中没有偶数,则需要具有单个值的流。

用例是:

Stream<Integer> result = defaultIfEmpty(integerList.stream()
                                 .filter(e -> e %2 == 0), () -> 99); 

这将产生一个

defaultIfEmpty(integerList.stream()
                       .filter(e -> e %2 == 0), () -> 99)
.map(x -> x*2)
...
...

或者在以下情况下应用后续的defaultIfEmpty方法:

 Stream<Integer> result = defaultIfEmpty(
                   defaultIfEmpty(integerList.stream()
              .filter(e -> e %2 == 0), 
      () -> 99).map(x -> x* 2)..., ()-> -1);

在这一点上,您可能会意识到易读性正在丢失,并且在您编写进一步的操作时仍将如此。

然而,这是最好的方法,因为我想不出任何其他方法来实现这一点,同时随着您在流中编写越来越多的方法,保持良好的易读性。

东方俊明
2023-03-14

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

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]
 类似资料:
  • 我有一个简单的代码,可以从第14列开始将转置的范围复制到另一张表的最后一行 它按原样返回零结果。如果我将destrow从公式更改为simple 2(这是现在最后一个空行),则效果很好。为什么不返回目标工作表中的最后一行索引?

  • 我正在尝试处理新的AndroidLollipopMediaProjection API。 我发现(至少在我的股票三星Galaxy S4 jfltexx上),当我开始意图获取捕获屏幕的权限()时,除非我在前面的尝试中选中了“不要再次询问”,否则在中不会有结果... 和结果处理: 权限对话框显示得很好,但是我的活动被隐藏了,它永远不会转到。 知道出什么问题了吗?

  • 虽然运行与SQL查询相同的查询并获取结果,但在ifCurrent中未获取任何结果。 我是否传递了错误的参数? 这就是在学校里通过的

  • 问题内容: 我有这个映射: 而这个查询: 我等待25个结果,因为我有25个后索引。但是我得到一个空集。如果我删除嵌套的过滤器,一切正常。我希望能够过滤嵌套对象 在我的设置中,我有: 我在这里缺少什么。 谢谢 问题答案: 简短版本: 尝试此操作(更新端点和索引名称后): 它对我有用,并且简化了您的设置。稍后,我将发布带有较长解释的编辑。 编辑:长版: 查询的问题是分析器与查询中的过滤器结合在一起。您

  • 问题内容: 这是我的架构 我做了这样的一个NEST查询: 并返回零元素! 如果我查看数据库(使用邮递员),则可以看到我的文档: 问题答案: 听起来您可能没有在为文档建立索引 之前 将类型的映射显式地放入索引中,所以Elasticsearch已基于所看到文档中字段的默认映射来推断该映射。例如,给定以下类型 如果我们按以下方式索引一些文档 在控制台中输出以下内容 我们没有匹配的文件。在Elastics