Java 8中的Spliterator(或类似工具),仅在存在单个值时返回该值。

5

我非常喜欢singleOrEmpty这个流操作符。虽然它不在标准库中,但我觉得它非常有用。如果一个流只有一个值,它会返回一个包含该值的Optional。如果它没有值或有多个值,它会返回Optional.empty()

Optional<Int> value = someList.stream().{singleOrEmpty}
[]     -> Optional.empty()
[1]    -> Optional.of(1)
[1, 1] -> Optional.empty()
etc.

我之前问过一个关于这个问题的问题,@ThomasJungblut提供了这个很棒的实现:

public static <T> Optional<T> singleOrEmpty(Stream<T> stream) {
    return stream.limit(2)
        .map(Optional::ofNullable)
        .reduce(Optional.empty(),
             (a, b) -> a.isPresent() ^ b.isPresent() ? b : Optional.empty());
}

唯一的问题是,您必须将其放在调用的开头。
singleOrEmpty(someList.stream().filter(...).map(...))

而不是在最后一个位置顺序执行

someList.stream().filter().map().singleOrEmpty()

相比于其他流处理机制,这使得阅读变得更加困难。

那么作为新手在流处理方面,有没有人有什么技巧来实现在一系列的流转换末尾添加一个短路singleOrEmpty机制?


我认为在Java 8中这是不可能的,类似的情况在这里(https://dev59.com/pWEh5IYBdhLWcg3whT3T)。 - Thomas Jungblut
1个回答

6

这种方法的速度不会像限制为2的那个快,但你可以使用它,方法如下:list.stream().filter(...).map(...).collect(singleOrEmpty())

static <T> Collector<T, ?, Optional<T>> singleOrEmpty() {
    return Collectors.collectingAndThen(
            Collectors.mapping(
                    Optional::of,
                    Collectors.reducing((a, b) -> Optional.empty())
            ),
            o -> o.orElseGet(Optional::empty)
    );
}

Stream.empty().collect(singleOrEmpty());   // Optional.empty
Stream.of(1).collect(singleOrEmpty());     // Optional[1]
Stream.of(1, 1).collect(singleOrEmpty());  // Optional.empty
Stream.of(1, 1).skip(1).collect(singleOrEmpty());  // Optional[1]

就我个人而言,除非这是真的需要高性能的代码,否则我更喜欢不那么聪明但更清晰的实现方式。

static<T> Collector<T,?,Optional<T>> singleOrEmpty() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            lst -> lst.size() == 1
                    ? Optional.of(lst.get(0))
                    : Optional.empty()
    );
}

2
第一个收集器可以使用重载版本的Collectors.reducing(...)进行简化。我认为这个版本不比你建议的第二个版本差(抱歉代码有点长,但我认为没有必要分开回答)。static Collector> singleOrEmptyCollector() { return Collectors.reducing( Optional.empty(), Optional::ofNullable, (opt, t) -> opt.isPresent() ^ t.isPresent() ? t : Optional.empty() ); } - Stanislav Lukyanov
2
@StanislavLukyanov 如果流中有3(或5、7等)个元素,您的收集器将失败。例如,Stream.of(1,1,1).collect(...)将返回Optional[1]而不是Optional.empty。 - Misha
谢谢!你说得对!实际上,我发现了一个更简单的技巧:Collectors.reducing((a, b) -> null)。希望这次我没有漏掉什么 :) - Stanislav Lukyanov
1
reducing((a,b) -> null) 可以工作!但感觉非常不对。stream.reduce 如果缩减的结果为 null,则会抛出 NPE,但 Collectors.reducing 不会。我想知道这是有意还是疏忽。使用此技巧的一个缺点是由单个 null 组成的流将显示为空。也许更健壮的解决方案是 mapping(Objects::requireNonNull, reducing((a,b) -> null)) - Misha
关于单个 null - 我们应该将这样的流视为空,因为返回类型是 Optional,只包含非空值。 - Stanislav Lukyanov
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接