繁体   English   中英

为什么不是Math.max(双a,双b)可变参数?

[英]Why isn't Math.max(double a, double b) variadic?

为什么Math.max的实现不具有可变函数

它可以像这样实现:

public class Main {
    public static double max(double... values) {
        double max = Double.NEGATIVE_INFINITY;
        for (double tmp : values) {
            max = max < tmp ? tmp : max;
        }
        return max;
    }

    public static void main(String[] args) {
        // This works fine:
        System.out.println(max(-13, 12, 1337, 9));

        // This doesn't work:
        // System.out.println(Math.max(-13, 12, 1337));
    }
}

是否有任何理由不这样实施?

早在Java 5中将可变函数引入语言之前,就已经在JDK 1.0中引入了java.lang.Math

另外,效率是一个问题:如果你在大多数时候需要两个元素,那么将它们“内联”传递它们要快得多,而不需要创建一个中间数组来保存它们。 这也避免了在实现中设置循环的成本。

虽然其他人已经回答了为什么Math.max不是可变参数,但他们没有回答为什么在引入可变参数函数时不会创建这样的方法。

我甚至不知道它(有一个开放的错误报告 )所以我只能猜测:

确实,它没有在Math实现,但如果我们查看Collections ,则有以下方法:

public static <T extends Object & Comparable<? super T>> T max(
    Collection<? extends T> coll) {
  ...
}

虽然类型签名看起来很丑陋(它需要足够灵活以处理协方差和逆变),但它可以很容易地与Collections.max(Arrays.asList(-13, 12, 1337, 9));一起使用Collections.max(Arrays.asList(-13, 12, 1337, 9)); 完成所有功能后,只需在不同的地方。

更好的是:此方法不仅可以处理双打,还可以处理实现Comparable接口的所有类型。

尽管如此,既没有建议的解决方案,也没有Collections的解决方案是面向对象的,它们只是静态方法。 幸运的是, JDK8将改变:

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

int max(List<Integer> list) {
  Optional<Integer> opt = list.stream().max((a,b) -> a-b);
  return opt.orElse(Integer.MAX_VALUE);
}

max(Arrays.asList(-13, 12, 1337, 9)); // 1337
max(Arrays.asList()); // 2147483647

对于即将发布的版本,集合库在Project Lambda中进行了重新设计,以更加面向对象。 在上面的示例中,Lambdas用于提供一种简单易读的方法来确定max元素。 以下也可以:

import static java.util.Comparators.naturalOrder;

Arrays.asList(-13, 12, 1337, 9)
  .stream()
  .max(naturalOrder())
  .ifPresent(System.out::println); // 1337

而不是max one也可以使用更高阶函数reduce

Arrays.asList(-13, 12, 1337, 9)
  .stream()
  .reduce((a,b) -> a > b ? a : b)
  .ifPresent(System.out::println); // 1337

另一个细节是使用Optional 它是一种简化错误处理的类型,由于高阶函数的组合,如上例所示。

lambda提议有几个优点,使得不必实现Math.max的可变形式:

  1. 它是面向对象的
  2. 它是多态的。 这意味着它可以用于每种类型的集合( ListSetStreamIterator等)
  3. 它富有表现力且易于理解
  4. 它允许动态并行化。 只需将.stream()更改为.parallelStream()

因为它存在的时间长于java中的可变函数(在java 5中引入)并且没有太多需要更新它,因为正如你刚刚所示,它自己做的很简单。

另外,varargs方法涉及性能损失,因为数组(double [])将从你的幕后参数创建

Java 8使用流实现了数字操作,这非常灵活。 一个例子:

DoubleStream.of( -13, 12, 1337, 9 ).max().getAsDouble()

不像自制软件那么简单,但仍然是直接的,快速的,更灵活的。

例如,使用多核只需要一个函数调用:

stream.parallel().max().getAsDouble()

在这种情况下毫无意义,因为即使使用double,查找max也非常快 - 您需要数百万的双倍才能看到毫秒的差异。 但如果还有其他处理,那么它可以快速加速。

或者您也可以使用系统类一次性找到最小值,平均值,总和等:

DoubleSummaryStatistics stat = DoubleStream.of( -13, 12, 1337, 9 ).summaryStatistics();
System.out.println( stat.getMin() );
System.out.println( stat.getAverage() );
System.out.println( stat.getMax() );
System.out.println( stat.getCount() );
System.out.println( stat.getSum() );

Math.max自JDK 1.0以来就已存在,早在引入变量#of argument语法之前。 这并不是说该方法无法按照您的建议更新。 有时库方法定义或实现会发生变化,但这种情况很少见。 大多数情况下,新方法被添加到类中而不是修改现有方法。

Max的新实现实际上是方法重载的一种情况,因为现有方法和新的var args方法可以在同一个类中并排存在。 因此,虽然它当然可以取代现有的方法,但它也可以只是Math类的一个补充。 所以我认为应该补充一下。 我们可以单独保留现有方法的事实消除了对新实现可能引发的性能的任何担忧。

无论如何,Java n和Java n + 1之间可以改变的文化正在发生变化。 例如,文件访问类和java.sql.Connection已从Java 6更改为Java 7,因为在Java 7中,它们现在实现了AutoCloseable Java 9实际上将从项目拼图方式中的类中删除一些方法。

我可以想到Math.max没有更新的正当理由。 也许直到现在还没人建议。 马克莱因霍尔德 ,你在读这个吗?

Math.max()历史可以追溯到JDK 1.0,而变量函数直到Java 5才存在。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM