问题
Why isn't the implementation of Math.max a variadic function?
It could get implemented like this:
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));
}
}
Is there any reason why it is not implemented like this?
回答1:
While others already have answered why Math.max
is not variadic, they didn't answer why such a method is not created when variadic functions are introduced.
I even don't know it (there is an open bug-report) so I can only guess:
It is true that it is not implemented in Math
, but if we look into Collections
there is the following method:
public static <T extends Object & Comparable<? super T>> T max(
Collection<? extends T> coll) {
...
}
While the type signature looks ugly (it needs to be flexible enough to handle covariance and contravariance), it can easily be used with Collections.max(Arrays.asList(-13, 12, 1337, 9));
After all the function is implemented, just in a different place.
Even better: This method can handle not only doubles, but all types implementing the Comparable
interface.
Nevertheless neither your suggested solution, nor the solution in Collections
is object oriented, they are just static methods. Luckily with JDK8 this will change:
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
For the upcoming release the collection library is reworked in Project Lambda to be more object oriented. In the example above, Lambdas are used to provide an easy and readable way to determine the max element. The following would work too:
import static java.util.Comparators.naturalOrder;
Arrays.asList(-13, 12, 1337, 9)
.stream()
.max(naturalOrder())
.ifPresent(System.out::println); // 1337
Instead of max
one could also use the higher order function reduce
:
Arrays.asList(-13, 12, 1337, 9)
.stream()
.reduce((a,b) -> a > b ? a : b)
.ifPresent(System.out::println); // 1337
Another detail is the use of Optional
. It is a type to simplify error handling due to composition of higher order functions as shown in the examples above.
The lambda proposal has several advantages that make it unnecessary to implement a variadic form of Math.max:
- It is object oriented
- It is polymorphic. This means it can be used with every type of collection (
List
,Set
,Stream
,Iterator
etc.) - It is expressive and easy to understand
- It allows on-the-fly parallelization. Just change
.stream()
to.parallelStream()
回答2:
The java.lang.Math
has been introduced in JDK 1.0, long before variadic functions were introduced into the language in Java 5.
In addition, efficiency is a concern: if you need two elements most of the time, it is much faster to pass them "inline", without creating an intermediate array to hold them. This also avoids the costs of setting up a loop inside the implementation.
回答3:
because its been there for longer than variadic functions exist in java (introduced in java 5) and there wasnt much demand to update it since, as you've just shown, its trivial to do yourself.
also, there' s ahidden performance penalty involved in varargs methods as an array (double[]) will be created from your arguments behind the scenes
回答4:
Java 8 has implemented number operations with streams, which is very flexible. An example:
DoubleStream.of( -13, 12, 1337, 9 ).max().getAsDouble()
Not as simple as homebrews, but still straight forward, fast, and more flexible.
For example, making use of multi-core need just one function call:
stream.parallel().max().getAsDouble()
Quite meaningless in this case, because finding max is very fast even with double - you need millions of doubles to see a difference of milliseconds. But if there are other processing then it can quickly speed them up.
Or you can also find min, average, sum etc. all in one go, using just system classes:
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() );
回答5:
Math.max has been around since JDK 1.0, long before the variable # of argument syntax was introduced. This is not to say that the method could not be updated the way you suggest. Sometimes library method definitions or implementations are changed, but this is rare. Most of the time new methods are added to classes rather than modifying existing methods.
Your new implementation of Max is really a case of method overloading because it is possible for the existing method and the new var args method to exist side by side in the same class. So while it can certainly replace the existing method, it could also just be an addition to the Math class. So I think it should be added. The fact that we can leave the existing method alone removes any concern about performance that the new implementation might raise.
The culture of what can get changed between Java n and Java n+1 is changing anyway. For instance, the File access classes and java.sql.Connection
were changed from Java 6 to Java 7 because in Java 7 they now implement AutoCloseable
. Java 9 is actually going to remove some methods from classes that are in the way of project jigsaw.
I can think of no valid reason for Math.max not being updated. Maybe no one suggested it until now. Are you reading this, Mark Reinhold?
回答6:
Math.max()
dates back to JDK 1.0, whereas variadic functions have not existed until Java 5.
来源:https://stackoverflow.com/questions/15040151/why-isnt-math-maxdouble-a-double-b-variadic