2013-02-23 21 views
5

Perché non è l'implementazione di Math.max a variadic function?Perché non è Math.max (double a, double b) varadic?

Si potrebbe ottenere implementata in questo modo:

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

C'è qualche motivo per cui non è implementata in questo modo?

+4

Math è stato introdotto in Java 1.0. Varargs in 1.5 ... – Kai

risposta

3

Mentre altri hanno già risposto perché il Math.max non è variadico, non hanno risposto perché un tale metodo non viene creato quando vengono introdotte funzioni variadiche.

ho anche non lo so (c'è un open bug-report) quindi posso solo immaginare:

E 'vero che non è implementato in Math, ma se guardiamo in Collections v'è il seguente metodo:

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

Mentre la firma di tipo sembra brutto (che deve essere sufficientemente flessibile per gestire covarianza e controvarianza), può essere facilmente utilizzato con Collections.max(Arrays.asList(-13, 12, 1337, 9)); Dopo aver implementato tutte le funzioni, proprio in un posto diverso.

Ancora meglio: questo metodo può gestire non solo i doppi, ma tutti i tipi che implementano l'interfaccia Comparable.

Tuttavia, né la soluzione suggerita, né la soluzione in Collections sono orientate agli oggetti, sono solo metodi statici. Per fortuna con JDK8 questo cambierà:

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 

Per la prossima versione della libreria collezione viene rielaborato in Project Lambda ad essere più orientato agli oggetti. Nell'esempio sopra, Lambdas viene utilizzato per fornire un modo facile e leggibile per determinare l'elemento massimo. Di seguito funzionerebbe ugualmente:

import static java.util.Comparators.naturalOrder; 

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

Anziché max si potrebbe utilizzare anche la funzione di ordine superiore reduce:

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

altro dettaglio è l'uso di Optional. È un tipo per semplificare la gestione degli errori a causa della composizione delle funzioni di ordine superiore, come mostrato negli esempi sopra.

La proposta lambda ha diversi vantaggi che rendono necessario implementare una forma di variadic Math.max:

  1. Si object oriented
  2. È polimorfico. Ciò significa che può essere utilizzato con ogni tipo di raccolta (List, Set, Stream, Iterator ecc)
  3. E 'espressiva e facile da capire
  4. Permette on-the-fly parallelizzazione. Basta cambiare .stream() a .parallelStream()
2

perché è lì da più tempo che le funzioni variadiche esistono in java (introdotto in java 5) e non c'era molta richiesta di aggiornamento da quando, come hai appena mostrato, è banale da fare da solo.

anche, ci' s pena Ahidden prestazioni coinvolti nei metodi varargs come un array (double []) verrà creato dai vostri argomenti dietro le quinte

+0

Sai con quale versione Java ha funzioni variadiche? Un'altra domanda: in che modo java decide quale funzione dovrebbe assumere se ci sono 'max (double ... values)' e 'max (double a, double b)'? L'ho provato e ci vuole quello non-variadico, ma non l'ho trovato nel JLS. –

+0

@moose - java 5 (ho incluso un collegamento). inoltre, come dasblinkenlight ha detto che i metodi vararg sono in realtà metodi che accettano un array - double [] in questo caso, quindi per soli 2 argomenti il ​​metodo non vararg è una corrispondenza migliore. – radai

5

Il java.lang.Math è stato introdotto in JDK 1.0, molto tempo prima che le funzioni variadic sono stati introdotti nella lingua in Java 5.

Inoltre, l'efficienza è una preoccupazione: se sono necessari due elementi la maggior parte del tempo, è molto più veloce passarli "in linea", senza creare un array intermedio per tenerli . Ciò evita anche i costi di creazione di un ciclo all'interno dell'implementazione.

0

Math.max() risale al JDK 1.0, mentre le funzioni di variadic non sono esistite fino Java 5.

1

Math.max è stato intorno dal JDK 1.0, molto prima che è stata introdotta la variabile # di sintassi dell'argomento. Questo non vuol dire che il metodo non possa essere aggiornato come suggerisci. A volte le definizioni dei metodi della libreria o le implementazioni sono cambiate, ma questo è raro. La maggior parte delle volte vengono aggiunti nuovi metodi alle classi piuttosto che modificare i metodi esistenti.

La nuova implementazione di Max è in realtà un caso di sovraccarico del metodo poiché è possibile che il metodo esistente e il nuovo metodo var args esistano affiancati nella stessa classe. Quindi, mentre può certamente sostituire il metodo esistente, potrebbe anche essere solo un'aggiunta alla classe Math. Quindi penso che dovrebbe essere aggiunto. Il fatto che possiamo lasciare il metodo esistente rimuove da solo ogni preoccupazione relativa alle prestazioni che potrebbe sorgere la nuova implementazione.

La cultura di ciò che può essere cambiato tra Java n e Java n + 1 sta comunque cambiando. Ad esempio, le classi di accesso ai file e java.sql.Connection sono state modificate da Java 6 a Java 7 perché in Java 7 ora implementano AutoCloseable. Java 9 in realtà sta per rimuovere alcuni metodi dalle classi che sono nel modo di project jigsaw.

Non riesco a pensare a un motivo valido per Math.max non aggiornato. Forse nessuno lo ha suggerito fino ad ora. Stai leggendo questo, Mark Reinhold?

2

Java 8 ha implementato operazioni di numero con flussi, che è molto flessibile. Un esempio:

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

Non semplice come gli homebrew, ma sempre semplice, veloce e flessibile.

Per esempio, facendo uso di multi-core bisogno solo una funzione di chiamata:

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

Piuttosto senso in questo caso, perché trovare max è molto veloce anche con il doppio - è necessario milioni di doppie di vedere un differenza di millisecondi. Ma se ci sono altre elaborazioni allora può velocizzarle rapidamente.

Oppure si può anche trovare min, media, somma ecc tutti in una volta, utilizzando solo le classi di sistema:

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