【问题标题】:lambda collecting within a ifPresent在 ifPresent 中收集 lambda
【发布时间】:2017-07-26 06:01:14
【问题描述】:

如何使用collect 方法在嵌套的ifPresent 方法中收集?

到目前为止,这是我所拥有的:

List result = list.stream()
                  .findFirst()
                  .ifPresent(info -> {
                      info.getMap()
                          .stream()
                          .filter( entry -> entry.getKey().equals("test"))
                          .map(entry::getValue)
                          .collect(Collectors.toList())                          
                   });

【问题讨论】:

  • 为什么必须是ifPresent?为什么不是flatMap
  • 或者更确切地说是 map(),在这种情况下。另外,不要使用原始类型。
  • 为什么要根据其键过滤 Map - 这意味着您正在迭代所有元素,并且存在 Map 以避免这种情况。那么为什么不直接使用 info.getMap().get("test") 呢? Optional<Object> result = list.stream().findFirst().flatMap(info -> Optional.ofNullable(info.getMap().get("test"))); 之类的东西——为什么你首先需要一个 List 作为结果类型?
  • 我在我的答案中添加了第二个编辑,其中包含一些 OP 可能尝试做的示例(基于上述 cmets)。我认为我们有一套全面的方法来回答他/她目前正在尝试做的任何类型的实现:)

标签: java lambda java-8 java-stream collectors


【解决方案1】:

您可能想这样做:

List result = list.stream()
    .limit(1)
    .flatMap(info -> // <-- removed brace {
        info.getMap().stream()
            .filter( entry -> entry.getKey().equals("test"))
            .map(entry::getValue)
    ) // <-- removed brace here too
    .collect(Collectors.toList());

让我解释一下:

.limit(1):将流限制在第一个元素(如果初始流为空,则返回空流)

.flatMap():将流映射到新流。在这种情况下,将返回一个由来自 Entry#getValue() 的值组成的新流

.collect(Collectors.toList()):最后将值流收集到一个列表中。

正如 Olivier Grégoire 在 cmets 中所说,以下方法也有效:

List result = list.stream()
    .limit(1)
    .flatMap(info -> info.getMap().stream())
    .filter( entry -> entry.getKey().equals("test"))
    .map(entry::getValue)
    .collect(Collectors.toList());

在我看来,这更易读、更清楚地表明了意图是什么。

【讨论】:

  • 我认为你应该调用 info.getMap().entrySet().stream(),而不是 info.getMap().stream(),以获取 Stream价值,对吧?
  • flatMap 接受流返回函数。你的内在 lambda 不会那样做。你应该.flatmap(info -&gt; info.getMap().stream()).filter(...).map(...).collect(...)
  • @OlivierGrégoire 我认为您的版本只是语义不同,但总的来说它们是相同的。我也更新了答案以显示您的方式:)
  • 你是对的。对我来说现在是清晨,我还没有喝咖啡。
【解决方案2】:

假设info.getMap 返回java.util.Map,我不明白为什么你需要内部流管道。

只需使用get("test")即可获取"test"键的值:

List result = 
    list.stream()
        .map(info -> info.getMap().get("test"))
        .filter(Objects::nonNull)
        .limit(1)
        .collect(Collectors.toList()); 

P.S.,不要使用原始类型。您应该将返回列表的类型更改为List&lt;ValueType&gt;,其中ValueTypeinfo.getMap() Map 中的值的类型。

【讨论】:

    【解决方案3】:

    看起来您有一个包含零个或多个Infos 的List&lt;Info&gt;,其中Info 是一个具有getMap() 方法的对象,该方法返回一个Map&lt;String, Something&gt;,而您希望得到一个@987654326 @,即第一个列表项的map中所有key为test的值。

    您希望map 可选而不是使用ifPresentifPresent 是终端操作,不返回任何内容(只是执行代码)。

    list // List<Info>
        .stream() // Stream<Info>
        .findFirst() // Optional<Info>
        .map(info -> info.getMap().entrySet() // Set<Entry<String, Something>>
             .stream() // Stream<Entry<String, Something>>
             .filter(entry -> entry.getKey().equals("test"))
             .map(Entry::getValue) // Stream<Something>
             .collect(toList()) // List<Something>
        ) // Optional<List<Something>>
        .orElse(Collections::emptyList) // List<Something>
    

    如果未指定最后一行,那么您将收到 Optional&lt;List&lt;Info&gt;&gt; 回复。

    编辑:Lino 对limit 的回答更为惯用,除非您真的想以这种方式使用Optional,否则请选择那个。

    编辑 2:

    假设您上面的代码是您的完整业务逻辑,您可能只是在列表的第一项中找到“test”的值(感谢 Lino 提供limit)。那是:

    list.stream() // Stream<List<Info>>
        .limit(1) // Stream<List<Info>> count 0/1
        .flatMap(info::getMap) Stream<Map<String, Something>> 0/1
        .map(map -> map.get("test")) Stream<Something> 0/1, possibly null
        .filter(Objects::nonNull) Stream<Something> 0/1
        .findFirst() Optional<Something>
    

    为了完整起见,如果您想在列表中的地图中找到“test”的任何值,它会是:

    list.stream() // Stream<List<Info>>
        .flatMap(info::getMap) Stream<Map<String, Something>> 
        .flatMap(map -> map.entrySet().stream()) // Stream<Entry<String, Something>>
        .filter(entry -> entry.getKey().equals("test"))
        .map(Entry::getValue) // Stream<Something>
        .collect(toList()) // List<Something>
    

    【讨论】:

    • entry.map(entry::getValue) 中是一个类吗?还是变量?
    • 这实际上是我从 OP 的代码中复制的一个错字。它应该是Entry::getValue(在Entry 上调用getValue 方法,当您调用entrySet() 时,iirc 是容器类型)。如果您像上面的filter 那样使用entry -&gt; entry.getValue(),它只会是entry。修正我的答案(但同样,Lino 的更惯用)。
    • 我觉得你的答案比我的好,不知道Optional#map() 甚至存在:)
    • 关于 OP 问题的 cmets 正在深入研究为什么他会这样做,但我认为我们都只是为了如何正确使用流而试图回答:)。我更喜欢你的,因为它消除了 ListOptional 之间不必要的转换(肯定更简洁)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-09
    • 2019-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多