一尘不染

Java 8收集器,如果只有一个值,则返回一个值

java

我对这种函数式编程和流技术有点陌生,但是我所知道的一点却非常有用!

我遇到过几次这种情况:

List<SomeProperty> distinctProperties = someList.stream()
    .map(obj -> obj.getSomeProperty())
    .distinct()
    .collect(Collectors.toList());

if (distinctProperties.size() == 1) {
    SomeProperty commonProperty = distinctProperties.get(0);
    // take some action knowing that all share this common property
}

我真正想要的是:

Optional<SomeProperty> universalCommonProperty = someList.stream()
    .map(obj -> obj.getSomeProperty())
    .distinct()
    .collect(Collectors.singleOrEmpty());

我认为,singleOrEmpty除了与结合使用外,该东西在其他情况下也可能有用distinct。当我成为超级n00b时,我花了很多时间重新发明Java
Collections
Framework,因为我不知道它在那里,所以我试图不重蹈覆辙。Java是否提供了执行此操作的好方法singleOrEmpty?我说错了吗?

谢谢!

编辑:这是distinct案例的一些示例数据。如果您忽略该map步骤:

Optional<SomeProperty> universalCommonProperty = someList.stream()
    .map(obj -> obj.getSomeProperty())
    .distinct()
    .collect(Collectors.singleOrEmpty());

[]     -> Optional.empty()
[1]    -> Optional.of(1)
[1, 1] -> Optional.of(1)
[2, 2] -> Optional.of(2)
[1, 2] -> Optional.empty()

当我搞砸我的类型或有旧代码时,我发现我需要这个。能够快速地说出“此集合的所有元素共享此属性,这真是太好了,因此现在我可以使用此共享属性采取一些措施。”
另一个示例是,当用户多重选择一些不同的元素,而您试图查看可以做的事情(如果有的话)对所有元素都有效。

EDIT2:对不起,如果我的例子有误导性。关键是 singleOrEmpty
。我通常会发现我放在distinct前面,但也可以很容易地成为filter其他类型的对象。

Optional<SomeProperty> loneSpecialItem = someList.stream()
    .filter(obj -> obj.isSpecial())
    .collect(Collectors.singleOrEmpty());

[special]           -> Optional.of(special)
[special, special]  -> Optional.empty()
[not]               -> Optional.empty()
[not, special]      -> Optional.of(special)
[not, special, not] -> Optional.of(special)

EDIT3:我想通过激励 singleOrEmpty 而不是 单独 要求它来搞砸。

Optional<Int> value = someList.stream().collect(Collectors.singleOrEmpty())
[]     -> Optional.empty()
[1]    -> Optional.of(1)
[1, 1] -> Optional.empty()

阅读 345

收藏
2020-12-03

共1个答案

一尘不染

仅评估前两个元素的“ Hacky”解决方案:

    .limit(2)
    .map(Optional::ofNullable)
    .reduce(Optional.empty(),
        (a, b) -> a.isPresent() ^ b.isPresent() ? b : Optional.empty());

一些基本的解释:

单个元素 [1]->映射到[Optional(1)]-> reduce

"Empty XOR Present" yields Optional(1)

=可选(1)

两个元素 [1,2]->映射到[Optional(1),Optional(2)]-> reduce可以:

"Empty XOR Present" yields Optional(1)
"Optional(1) XOR Optional(2)" yields Optional.Empty

=可选。空

这是完整的测试用例:

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());
}

@Test
public void test() {
    testCase(Optional.empty());
    testCase(Optional.of(1), 1);
    testCase(Optional.empty(), 1, 1);
    testCase(Optional.empty(), 1, 1, 1);
}

private void testCase(Optional<Integer> expected, Integer... values) {
    Assert.assertEquals(expected, singleOrEmpty(Arrays.stream(values)));
}

对于贡献XOR想法和上述测试用例的Ned(OP)表示感谢!

2020-12-03