【问题标题】:Maximum of Stream with custom Comparator具有自定义比较器的最大流
【发布时间】:2014-04-24 03:33:17
【问题描述】:

下面是我专门为在 Java 8 Stream 中使用自定义 Comparatormax 而编写的代码。

import java.math.BigDecimal;
import java.util.*;

public class BigDecimalMax  {

    public static BigDecimal getBigDecimalMax(List<forTest> list) {

        return list.stream()
            .filter(t -> t.id % 2 == 0)
            .max(forTestComparator::compare)  //<-- syntax error ----------
            .orElse(null);
    }

    public static class forTestComparator implements Comparator<forTest> {

        @Override
        public int compare(forTest val1, forTest val2) {
            return val1.value.compareTo(val2.value);
        }
    }

    public static void main(String[] args) {

        List<forTest> lst = new ArrayList<>();
        Random rn = new Random();
        BigDecimalMax bdm = new BigDecimalMax();

        for (int i=1; i<22; ++i) {
            lst.add(bdm.new forTest(i, BigDecimal.valueOf(rn.nextLong())));
        }

        System.out.println(getBigDecimalMax(lst));

    }

    class forTest {
        public int id;
        public BigDecimal value;

        forTest(int id, BigDecimal value) {
            this.id = id;
            this.value = value;
        }

        @Override
        public String toString() {
            return "forTest{" +
                    "id=" + id +
                    ", value=" + value +
                    '}';
        }
    }
}

我在一个我不理解的方法引用上遇到语法错误。

Error:(15, 18) java: incompatible types: invalid method reference
    cannot find symbol
      symbol:   method compare(BigDecimalMax.forTest, BigDecimalMax.forTest)
      location: class BigDecimalMax.forTestComparator

而 IntelliJ IDEA 抱怨 Non-static method cannot be referenced from a static context

我到底做错了什么?


补充说明(2014 年 4 月 24 日):

  1. 我现在明白语法错误的原因了。谢谢。

  2. 这里真的需要自定义Comparator吗?

因为BigDecimal 实现了Comparable 似乎实现了Comparator(它有CompareTo()没有 Compare())我认为自定义Comparator 是必要的。这就是为什么我不能只使用Comparator.comparing(ft -&gt; ft.value)。我的逻辑有问题吗?

【问题讨论】:

  • 这里不需要自定义比较器。首先,这是 API 的一个令人困惑的领域,因为名称都非常相似。首先,BigDecimal 实现了 Comparable 是正确的,但 Stream.max() 需要一个 Comparator。 Comparator.comparing() 的单参数重载就是这样做的。此外,ForTest 类(原始版本)也没有实现。所以你需要提供一个 lambda ft -&gt; ft.value 来从 ForTest 中提取一个 BigDecimal(它是 Comparable),然后将此 lambda 传递给单参数 Comparator.comparing() 以在给定 Comparable 实例的情况下创建一个 Comparator。
  • @StuartMarks - 我实际上读了几次comparing description,但只有在阅读了你的答案(也是好几次)之后,我才意识到它的意思。所以,comparaing 充当了ComparatorComparable 之间的桥梁,对吧?
  • 是的,这是描述它的好方法。不幸的是,签名中的文档和泛型非常复杂,因此仅通过阅读文档来学习用法非常困难。然后你看到一个例子然后“啊哈!”我希望。

标签: java max java-8 comparator java-stream


【解决方案1】:

Sotirios Delimanolis' answer 展示了如何解决问题,但我还有一些事情要补充。

如果您已经有一个实现 Comparator 的类,则不需要使用对其 compare() 方法的方法引用。你可以直接传递它的一个实例,因为 max() 需要一个对 Comparator 的引用:

    .max(new forTestComparator())

    forTestComparator instance = new forTestComparator();
    ...
    .max(instance)

然而,Comparator 上的组合函数通常使得没有必要拥有一个实现 Comparator 的类。例如,您可以完全摆脱 forTestComparator 类,然后这样做:

    .max(Comparator.comparing(ft -> ft.value))

或者如果 forTest 有明显的 getValue() 方法,可以重写流 max() 调用如下:

    .max(Comparator.comparing(forTest::getValue))

另外,如果你想让forTest实现Comparable接口,你可以这样做:

public class forTest implements Comparable<forTest> {
    @Override
    public int compareTo(forTest other) {
        return this.value.compareTo(other.value);
    }
    ...
}

在 Comparable 上使用 max() 的方法是:

    .max(Comparator.naturalOrder())

两种风格说明:

  1. 我强烈反对在 Optional 的实例上使用 orElse(null)。这是允许的,尽管它的主要目的可能是将新 Java 8 API 的使用改造成期望 null 表示缺少值的代码。如果可能,请避免使用orElse(null),因为这会强制调用者检查是否为空。相反,用一个实际值替换一个缺失值,或者将Optional 本身返回给调用者,这样调用者就可以应用它想要的任何策略。

  2. 我建议坚持使用大写、混合大小写的类名的既定 Java 命名约定。类名 forTestforTestComparator 使这段代码难以使用,因为它们看起来不像类名。

【讨论】:

  • 谢谢。我通过在原始帖子中添加我的推理来解决您的一个建议。你能看看吗?
【解决方案2】:

forTestComparator#compare 是一个实例方法。您需要一个 instance 方法引用,而不是您拥有的 static 方法引用。

类似

new forTestComparator()::compare

或者很长的路(你的类没有实例状态,所以你并不真正关心引用)

forTestComparator instance = new forTestComparator(); // fix for Java conventions
return list.stream()
        .filter(t -> t.id % 2 == 0)
        .max(instance::compare)  //<-- syntax error ----------
        .orElse(null);

请参阅有关方法引用的 Java 教程here

引用特定对象-&gt; ContainingObject::instanceMethodName的实例方法


旁注,这个

return list.stream()
            .filter(t -> t.id % 2 == 0)
            .max(new forTestComparator()::compare)  
            .orElse(null);

解析为forTest 值。您需要更改方法的返回类型。

【讨论】:

  • +1 用于解决问题,并查看我的答案以获取更多信息。
  • 特别感谢旁注。一开始我还没有意识到它的重要性。
猜你喜欢
  • 1970-01-01
  • 2021-02-22
  • 2012-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-13
  • 2012-02-11
  • 2017-11-02
相关资源
最近更新 更多