【问题标题】:How to iterate nested for loops referring to parent elements using Java 8 streams?如何使用 Java 8 流迭代引用父元素的嵌套 for 循环?
【发布时间】:2015-03-24 14:43:28
【问题描述】:

我想使用 java8 streams 迭代嵌套列表,并在第一次匹配时提取列表的一些结果。 不幸的是,如果子元素与过滤器匹配,我还必须从父内容中获取值。

我该怎么做?

java7

Result result = new Result();

//find first match and pupulate the result object.
for (FirstNode first : response.getFirstNodes()) {
    for (SndNode snd : first.getSndNodes()) {
        if (snd.isValid()) {
            result.setKey(first.getKey());
            result.setContent(snd.getContent());
            return;
        }
    }
}

java8

 response.getFirstNodes().stream()
        .flatMap(first -> first.getSndNodes())
        .filter(snd -> snd.isValid())
        .findFirst()
        .ifPresent(???); //cannot access snd.getContent() here

【问题讨论】:

标签: java java-8 java-stream optional


【解决方案1】:

当您需要这两个值并想要使用flatMap(当您想要执行像findFirst 这样的短路操作时需要),您必须映射到一个包含这两个值的对象

response.getFirstNodes().stream()
  .flatMap(first->first.getSndNodes().stream()
    .map(snd->new AbstractMap.SimpleImmutableEntry<>(first, snd)))
  .filter(e->e.getValue().isValid())
  .findFirst().ifPresent(e-> {
    result.setKey(e.getKey().getKey());
    result.setContent(e.getValue().getContent());
  });

为了只使用标准类,我使用Map.Entry 作为 Pair 类型,而真正的 Pair 类型可能看起来更简洁。

在这个特定的用例中,您可以将过滤操作移至内部流

response.getFirstNodes().stream()
  .flatMap(first->first.getSndNodes().stream()
     .filter(snd->snd.isValid())
     .map(snd->new AbstractMap.SimpleImmutableEntry<>(first, snd)))
  .findFirst().ifPresent(e-> {
    result.setKey(e.getKey().getKey());
    result.setContent(e.getValue().getContent());
  });

它有一个简洁的效果,只有一个匹配的项目,Map.Entry 实例将被创建(嗯,应该作为the current implementation is not as lazy as it should,但即使那样它仍然会创建比使用更少的对象第一个变体)。

【讨论】:

  • 感谢您的解释。查看您的代码,我觉得最好还是坚持使用 java7 风格的迭代样式,以防必须访问多个/父元素。
  • 是的,旧的 for 循环并没有过时。也许未来的 Java 版本支持对/元组的语言支持为更好的流解决方案提供了可能性……
【解决方案2】:

应该是这样的:

编辑:感谢 Holger 指出代码不会在第一个有效的 FirstNode 处停止

response.getFirstNodes().stream()
  .filter(it -> {it.getSndNodes().stream().filter(SndNode::isValid).findFirst(); return true;})
  .findFirst()
  .ifPresent(first -> first.getSndNodes().stream().filter(SndNode::isValid).findFirst().ifPresent(snd -> {
    result.setKey(first.getKey());
    result.setContent(snd.getContent());
  }));

可以找到测试here

【讨论】:

  • 这没有任何用处。该问题的原始代码称为result.setKey(first.getKey()); result.setContent(snd.getContent()); 并停止处理后续项目。你的代码没有那样做。
  • 我在展示如何在ifPresent() 中访问snd.getContent(),现在已经修改了代码
  • @Holger 我可以把 -1 去掉吗,我已经测试过我的代码可以正常工作并且符合操作的预期
  • 我没有投反对票,所以我对此无能为力。除此之外,使用forEach 仍然不能解决问题的任务,即在第一场比赛后停止。当然,仅使用一个匹配项进行测试不会表现出这种行为……
  • 没问题,感谢您指出我的代码中的错误。我现在已经修复了代码来做正确的事情。我会把答案留在那里,因为它是正确的。无论谁认为答案值得一票否决,如果我被告知原因会有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-19
  • 2016-05-14
  • 2019-04-05
相关资源
最近更新 更多