2015-09-02 19 views
12

Supponiamo che voglia eseguire un'iterazione su una raccolta di oggetti.Differenze di implementazione/ottimizzazioni tra espressioni Lambda e classi anonime

List<String> strmap = ... 
//Without lambdas 
strmap.stream().filter(new Predicate<String>() { 
     public boolean test(String string) { 
      return string.length == 10; 
     } 
}.forEach(new Consumer<String>() { 
     public void accept (String string) { 
      System.out.println("string " + string + " contains exactly 10 characters"); 
     } 
} 
//With lambdas 
strmap.stream() 
     .filter(s -> s.length == 10) 
     .forEach(s -> System.out.println("string " + s + " contains exactly 10 characters"); 

Come funziona il secondo esempio (senza lambda)? Un nuovo oggetto (Predicate e Consumer) creato ogni volta che chiamo il codice, quanto può il compilatore java jit ottimizzare l'espressione lambda? Per una performance migliore dovrei dichiarare tutti i lambda come una variabile e sempre solo passare un riferimento?

private Predicate<String> length10 = s -> s.length == 10; 
private Consumer<String> printer = s -> { "string " + s + " contains exactly 10 characters"; } 

strmap.stream() 
     .filter(length10) 
     .forEach(printer); 
+3

Vedere [here] (http://stackoverflow.com/q/27524445/2711488). E [questo] (http://stackoverflow.com/a/23991339/2711488) si applica anche alle espressioni lambda e ai riferimenti al metodo. HotSpot può ottimizzare le istanze temporanee delle classi interne, ma per le espressioni lambda stateless non ci sono istanze temporanee in primo luogo. – Holger

+1

@Holger - belle risposte – ZhongYu

risposta

6

Come funziona il secondo esempio (senza lambda) funziona? Un nuovo oggetto (Predicate e Consumer) creato ogni volta che chiamo il codice, quanto può il compilatore java jit ottimizzare l'espressione lambda? Per una performance migliore dovrei dichiarare tutti i lambda come una variabile e sempre solo passare un riferimento?

Il lavoro del JIT è di farlo in modo da non doversi preoccupare di nulla di tutto questo. Ad esempio, il JIT può (e, credo, lo farà) fare in modo che s -> s.length == 10 diventi automaticamente una costante statica, quindi viene riutilizzata in tutta la classe, non solo in quell'istanza. Potrebbe anche essere riutilizzato in altre classi che usano lo stesso lambda da qualche altra parte.

L'intero punto di lambda è che si dovrebbe semplicemente scrivere il codice più semplice che abbia senso e il JIT ha la libertà di fare tutto il necessario per renderlo efficiente. Ad esempio, ecco perché i lambda vengono generati usando invokedynamic invece di essere semplicemente desugared alle classi interne anonime in primo luogo.

Scrivere il codice semplice; confida che il JIT faccia la cosa giusta. Ecco a cosa serve. Detto questo, se vuoi i dettagli opprimenti e complicati, il numero this è quasi certamente la fonte migliore.

+3

Poiché 's -> s.length() == 10' è invariante, diventa sempre una costante, senza bisogno dell'aiuto del JIT. [Questo] (http://stackoverflow.com/q/27524445/2711488) si applica anche alla modalità interpretata ... – Holger

+0

Sto leggendo il documento collegato specificando l'implementazione. AFAICT, 's -> s.length() == 10' viene abbassato in un metodo statico privato, ma ho ancora l'impressione che il JIT implementa il metafactory lambda che decide come convertirlo nell'istanza dell'interfaccia? –

+2

No, il ['LambdaMetafactory'] (http://docs.oracle.com/javase/8/docs/api/?java/lang/invoke/LambdaMetafactory.html) è una normale classe Java che genera un codice bytecode che utilizza il pozzo nota libreria ASM. È anche possibile passarlo attraverso un debugger Java quando un'espressione lambda viene istanziata la prima volta. Ma a causa di come funziona l'istruzione 'invokedynamic', le esecuzioni successive useranno il risultato di quella prima invocazione che è o un handle di una costante o una invocazione di metodo/costruttore factory. – Holger