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

时间:2013-02-23 11:43:01

标签: java variadic-functions

为什么Math.max的实现不是variadic function

它可以像这样实现:

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));
    }
}

有没有理由不这样实现?

6 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:3)

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

我甚至不知道它(有一个open bug-report)所以我只能猜测:

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

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

虽然类型签名看起来很丑陋(它需要足够灵活以处理协方差和逆变),但它可以很容易地与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也可以使用更高阶函数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()
  5. 即可

答案 2 :(得分:2)

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

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

答案 3 :(得分:2)

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

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

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

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

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

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

或者你也可以一次性使用系统类找到min,average,sum等:

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() );

答案 4 :(得分:1)

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实际上将从project jigsaw的方式中删除某些方法。

我可以想到Math.max没有更新的正当理由。也许直到现在还没人建议。你在读这个,Mark Reinhold吗?

答案 5 :(得分:0)

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