【问题标题】:Java 8 forEach applied to only some?Java 8 forEach 只适用于一些?
【发布时间】:2018-03-25 17:49:48
【问题描述】:

请不要使用“if”语句,除非您要解释为什么不能没有“if”语句。

我看到了仅在流上我能走多远。我有这个麻烦:

List<Cube> revised =
    cubes.filter(p)
    .map(c -> f(c))
    .map(c -> {
        if(c.prop()) {
           c.addComment(comment);
        }
        return c;
    })
    .collect(Collectors.toList());

我最好的想法是如何在没有“如果”的情况下做到这一点

List<Cube> revised = 
    cubes.filter(p)
    .map(c -> f(c));

revised
    .filter(Cube::prop)
    .forEach(c -> c.addComment(comment)); // can also map still

有没有办法只在一个链中做到这一点?如果是这样,分支基本上必须在流中发生。像forSome(predicate, lambda) 这样的方法可以工作。

不想“自己动手”任何事情。我可以使用“如果”,但我正在尝试了解功能样式的表现力。

【问题讨论】:

  • Optional.ifPresent 可以接受吗?
  • @VGR 正是我的想法 :)
  • Collectors.partitioningBy 如果您想通过Cube::prop“开球”您的流,可能会有所帮助
  • 这需要拆分和合并流——Java Stream 库不这样做。不过,其他库也支持它。
  • f(c) 返回什么?

标签: java java-8 java-stream


【解决方案1】:

当您拥有peek 时,无需使用返回相同元素的map。以下代码通过使用短路运算符“作弊”:

cubes.filter(p)
    .map(c -> f(c))
    .peek(c -> c.prop() && c.addComment(comment))

我认为使用 Optional 的“现代”方式的可读性要差得多:

cubes.filter(p)
    .map(c -> f(c))
    .peek(c -> Optional.of(c).filter(Cube::prop).ifPresent(c -> c.addComment(comment)))

【讨论】:

  • 假设addComment 返回boolean
  • Optional.of(c) 样式是不是有问题?它似乎嵌套了一个 lambda 而不是链一个,即第一个“c”被第二个“c”遮蔽,即使它们在逻辑上是相同的。
  • @djechlin:当然,当范围内已经有c 时,声明一个新的c 是行不通的。您将不得不使用不同的名称。
【解决方案2】:

您可以通过以下方式实现您的forSome 函数:

public static <T> T forSome(T c, Predicate<T> condition, Consumer<T> extraBehaviour) {

    if (condition.test(c)) {
        extraBehaviour.accept(c);
    }

    return c;
}

您可以使用 map 运算符将其注入流中:

   List<Cube> revised = cubes.stream().filter(p)
            .map(c -> f(c))
            .map(c -> forSome(c, Cube::prop, cube -> cube.addComment("my comment 2")))
            .collect(Collectors.toList());

再举一个例子,我们可以举以下例子:

class StudentExam {
    private final String studentName;
    private final List<Character> answers;
    private boolean passed = false;


    StudentExam(String studentName, List<Character> answers) {
        this.studentName = studentName;
        this.answers = answers;
    }

    public void markAsPassed() {
        this.passed = true;
    }

    public boolean isPassed() {
        return passed;
    }

    public Character getAnswer(int index) {
        return answers.get(index);
    }

    public String getStudentName() {
        return studentName;
    }
}

   List<StudentExam> results = asList(
            new StudentExam("John", asList(new Character[] {'A', 'B'})),
            new StudentExam("Andy", asList(new Character[] {'A', 'C'})),
            new StudentExam("Mary", asList(new Character[] {'B', 'B'})),
            new StudentExam("Jane", asList(new Character[] {'C', 'D'}))
            );

如果正确答案是“A”和“B”,现在我们可以通过对象流式传输并设置适当的考试状态。

    results.stream()
            .map(examResult -> forSome(
                    examResult,
                    er -> er.getAnswer(0).equals('A') || er.getAnswer(1).equals('B'),
                    StudentExam::markAsPassed))
            .forEach(studentExam -> 
                     studentExam.getStudentName() + " passed: " + studentExam.isPassed()));

打印:

  • 约翰:通过了正确的
  • 安迪:通过了真的
  • 玛丽:通过了真正的
  • 简:通过假

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-22
    • 2018-05-03
    • 1970-01-01
    • 2015-01-09
    • 1970-01-01
    • 2015-03-18
    • 2015-10-06
    相关资源
    最近更新 更多