2015-09-02 13 views
12

Pozwala założyć, że chcę iterować nad kolekcją obiektów.Różnice w implementacji/optymalizacje między wyrażeń Lambda i anonimowych klas

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

Jak działa drugi przykład (bez lambdas)? Nowy obiekt (Predicate i konsument) tworzony za każdym razem, gdy wywołuję kod, w jakim stopniu kompilator java jit może zoptymalizować wyrażenie lambda? Dla lepszej wydajności powinienem zadeklarować wszystkie lambdy jako zmienną i zawsze przekazywać tylko referencje?

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

Zobacz [tutaj] (http://stackoverflow.com/q/27524445/2711488). I [this] (http://stackoverflow.com/a/23991339/2711488) dotyczy również wyrażeń lambda, a także odwołań do metod. HotSpot może zoptymalizować tymczasowe wystąpienia klas wewnętrznych, ale w przypadku wyrażeń bezstanowych lambda nie ma tymczasowych instancji w pierwszej kolejności. – Holger

+1

@Holger - ładne odpowiedzi – ZhongYu

Odpowiedz

6

jaki sposób drugi przykład (bez lambdas) działa? Nowy obiekt (Predicate i konsument) tworzony za każdym razem, gdy wywołuję kod, w jakim stopniu kompilator java jit może zoptymalizować wyrażenie lambda? Dla lepszej wydajności powinienem zadeklarować wszystkie lambdy jako zmienną i zawsze przekazywać tylko referencje?

Zadaniem JIT jest sprawienie, abyś nie musiał się o to martwić. Na przykład JIT może (i, jak sądzę, zrobi to), aby automatycznie stała się stała statyczna, więc jest ponownie wykorzystywana w całej klasie, a nie tylko w tej instancji. Może nawet zostać ponownie użyty przez inne klasy, które używają tego samego lambda gdzie indziej.

Cały punkt lambdas polega na tym, że powinieneś napisać najprostszy kod, który ma sens, a JIT ma swobodę robienia wszystkiego, co jest potrzebne, aby uczynić to wydajnym. Na przykład, dlatego lambdy są generowane przy użyciu invokedynamic zamiast być po prostu zagłębione w anonimowych klasach wewnętrznych.

Napisz prosty kod; ufaj JIT, aby zrobił dobrze. Po to tam jest. To powiedziawszy, jeśli chcesz, aby przytłaczające, skomplikowane szczegóły w stylu nitty, this jest prawie na pewno najlepszym źródłem.

+3

Ponieważ 's -> s.length() == 10' jest niezmienne, zawsze staje się stałe, bez potrzeby pomocy JIT. [This] (http://stackoverflow.com/q/27524445/2711488) odnosi się nawet do trybu interpretacji ... – Holger

+0

Czytam poprzez połączony dokument określający implementację. AFAICT, 's -> s.length() == 10' jest obniżany do prywatnej metody statycznej, ale nadal mam wrażenie, że JIT implementuje metafactory lambda, która decyduje o konwersji tego parametru do instancji interfejsu? –

+2

Nie, ['LambdaMetafactory'] (http://docs.oracle.com/javase/8/docs/api/?java/lang/invoke/LambdaMetafactory.html) jest zwykłą klasą Javy, która generuje kod bajtowy za pomocą studni - znana biblioteka ASM. Możesz nawet przejść przez niego za pomocą debugera Java, gdy za pierwszym razem utworzone zostanie wyrażenie lambda. Ale ze względu na sposób działania instrukcji 'invokedynamic' kolejne wywołania będą używać wyniku tego pierwszego wywołania, które jest albo uchwytem do stałej, albo do fabrycznego wywołania metody/konstruktora. – Holger

Powiązane problemy