【问题标题】:How to iterate nested for loops referring to parent elements using Java 8 Streams and Lambdas?如何使用 Java 8 Streams 和 Lambda 迭代引用父元素的嵌套 for 循环?
【发布时间】:2017-02-07 02:48:25
【问题描述】:

我有以下代码。

public static ModuleKey getDeployableModuleFromModulesList(List<Module> modules) {
        ModuleKey deployableModuleKey = null;
        for(Module module : modules) {
            List<Artifact> artifacts = module.getArtifacts();
            for(Artifact artifact : artifacts) {
                if(artifact.getType().equals("ear")) {
                    return module.getKey();
                } else if(!artifact.getType().equals("ear")) {
                    if(artifact.getType().equals("war")) {
                        deployableModuleKey = module.getKey();
                    } 
                }
            }
        }
        return deployableModuleKey;
    }

此代码查找 key='ear' 的第一个 'deployableModuleKey' 或 key = 'war' 的最后一个。 我想使用 Java 8 Streams 和 Lambdas 实现与上述相同的功能。

到目前为止,我尝试过的是:

modules.stream().flatMap(e -> e.getArtifacts().stream())
  .filter(e -> e.getType().equals("ear")).findFirst()
  .orElseGet(() -> modules.stream().flatMap(e -> e.getArtifacts().stream())
  .filter(e -> e.getType().equals("war")).reduce((a, b) -> b).orElse(null));

上面的代码 sn-p 将返回一个 Artifact 类型的对象,而不是 Module。我想获得工件与条件匹配的那个模块。找到模块后,我可以通过 module.getKey() 返回密钥。 为此,我想知道我们如何引用父 for 循环的元素。

我不确定我的 Java 8 代码是否完全正确。
有人可以在这方面帮助我吗?

【问题讨论】:

  • 我建议您将这个过程分解为几个函数。这并不意味着不鼓励 Java 8 流链接操作,而是尽可能使它们清晰和原子。
  • 我怀疑使用流比你的实际代码更好。流不适合在中间操作之间保持状态。另外,您当前的代码通过所有工件一次通过,而提议的解决方案通过两次。如果您只想通过一次完成此操作,我建议您创建自己的收集器(这里有很多关于创建收集器的问题,只需选择一个并从那里继续)

标签: lambda java-8 java-stream


【解决方案1】:

我想你知道多余的 if 签入你的旧 Java 样式代码。

我已经通过分解成任何人都能理解的更小、更简单的函数来编写你的大型单一操作流。

public static ModuleKey getDeployableModuleFromModulesList(List<Module> modules) {

    return findEARKey(modules.stream())
            .orElse(findWARKey(modules.stream()).orElse(null));

}

public static Optional<ModuleKey> findEARKey(Stream<Module> moduleStream){

    return  moduleStream.flatMap(e -> e.getArtifacts().stream())
            .filter(e -> e.getType().equals("ear"))
            .map(Artifact::getKey).findFirst();
}

public static Optional<ModuleKey> findWARKey(Stream<Module> moduleStream){

    return  moduleStream.flatMap(e -> e.getArtifacts().stream())
            .filter(e -> e.getType().equals("war"))
            .map(Artifact::getKey).findFirst();
}

【讨论】:

  • 如果我想要 type='ear' OR 'war' 的'deployableModuleKey',我认为你的代码很好。但是,我想要的是第一个'deployableModuleKey'类型='耳朵'。如果ear 不存在,那么我想返回最后一个带有 type= 'war' 的 moduleKey。
  • 此外,此代码返回工件密钥而不是模块密钥
  • 是的,Artifact 对象中没有名为key 的属性。
【解决方案2】:

关于流的问题是,一旦您使用诸如filtermap 之类的中间操作修改了您的流,您就不能再引用前一个操作中的值。因此,您必须找到解决方法才能始终保持(在您的情况下为Module)您想要在流中引用的值。在以下解决方案中,我使用 anyMatch 操作打开两个辅助流,如果模块包含包含所需密钥的工件,则会给出正值。

这应该可以解决问题:

ModuleKey key = Optional.ofNullable(modules.stream().filter(m -> m.getArtifacts().stream()
    .anyMatch(a -> a.equals("ear"))).findFirst().orElse(modules.stream()
    .filter(m -> m.getArtifacts().stream().anyMatch(a -> a.equals("war")))
    .reduce((a, b) -> b).orElse(null))).map(Module::getKey).orElse(null);

要查找第一个元素,请使用findFirst,它会返回一个Optional&lt;T&gt;。我们将OptionalOptional#orElse 链接起来,它解开它,如果它是空的,则返回另一个值(在这种情况下使用reduce((a, b) -&gt; b) 来查找最后一个元素)。如果没有找到任何东西,我们不希望代码抛出NullPointerException。因此,在调用 getKey 方法之前,我们将所有这些包装到带有 Optional.ofNullableOptional 中,并将 orElse 设置为 null,这是您的代码中指定的默认值。

顺便说一句,

else if(!artifact.getType().equals("ear")) {
    if(artifact.getType().equals("war")) {
    }
}

可以简化为

else if(artifact.getType.equals("war")) {
}

【讨论】:

  • 如果我使用您的代码 sn-p,我会收到以下编译错误。 The method filter((&lt;no type&gt; m) -&gt; {}) is undefined for the type List&lt;Module&gt;The method getKey() is undefined for the type ModuleUtilgetKey() 方法是 Module 对象的一部分。
  • @RITZXAVI 我在 notepad++ 中而不是我的普通 IDE 中对我的代码进行了原型设计,并且出现了新的故障。我编辑了我的答案以修复它们,如果您仍然遇到问题,请告诉我。
  • 现在我得到这个错误。 The method orElse(Module) in the type Optional&lt;Module&gt; is not applicable for the arguments (Optional&lt;Module&gt;)
  • @RITZXAVI 呼!最后在我的IDE中编辑它,应该这样做。错误是我正在喂orElse,它需要一个Module,返回值为findFirst,这是一个Optional。我只是用另一个orElse 解开了最后一个Optional。如果您还有问题,请再次告诉我。
  • 虽然它在语法上是正确的,但在功能方面,它并没有达到预期的效果。无论如何,我已经想出了我将发布的解决方案。你可以检查一下。不过感谢您的尝试。
【解决方案3】:

我已经找到了解决问题的方法。

public static ModuleKey getDeployableModuleFromModulesList(List<Module> modules) {
    Optional<ModuleKey> op = modules.stream().filter(module -> module.getArtifacts().stream().anyMatch(artifact -> artifact.getType().equals("ear"))).map(module -> module.getKey()).findFirst();
    if (!op.isPresent()) {
        op = modules.stream().filter(module -> module.getArtifacts().stream().anyMatch(artifact -> artifact.getType().equals("war"))).map(module -> module.getKey()).reduce((a, b) -> b);
    }
    return op.orElse(null);
}

无论如何,也欢迎其他解决方案。如果有人添加其他解决方案,我会尝试所有解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-19
    • 2022-01-14
    • 1970-01-01
    相关资源
    最近更新 更多