Java 8 Iterable.foreach () vs foreach loop

Która z poniższych metod jest lepsza w Javie 8?

Java 8:

joins.forEach(join -> mIrc.join(mSession, join));

Java 7:

for (String join : joins) {
    mIrc.join(mSession, join);
}

Mam wiele pętli for, które można by "uprościć" z lambda, ale czy naprawdę jest jakaś korzyść z ich używania? Czy poprawi to ich wydajność i czytelność?

EDIT

Poszerzę to pytanie również o dłuższe metody. Wiem, że nie można zwracać lub łamać funkcji rodzica z lambda i to powinno być również brane pod uwagę rozważenie przy ich porównywaniu, ale czy jest coś jeszcze do rozważenia?

Author: Rafael, 2013-05-19

8 answers

Korzyść jest brana pod uwagę, gdy operacje mogą być wykonywane równolegle. (Patrz http://java.dzone.com/articles/devoxx-2012-java-8-lambda-and - sekcja o iteracji wewnętrznej i zewnętrznej)

  • Główną zaletą z mojego punktu widzenia jest to, że implementacja tego, co ma być zrobione w pętli, może być zdefiniowana bez konieczności decydowania, czy będzie wykonywana równolegle czy sekwencyjnie

  • Jeśli chcesz, aby Twoja pętla została wykonana w równolegle możesz po prostu napisać

     joins.parallelStream().forEach(join -> mIrc.join(mSession, join));
    

    Będziesz musiał napisać dodatkowy kod do obsługi wątków itp.

Notatka: dla mojej odpowiedzi założyłem joins implementujący Interfejs java.util.Stream. Jeśli joins implementuje tylko Interfejs java.util.Iterable, nie jest to już prawdą.

 157
Author: mschenk74,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-29 07:40:06

Lepszą praktyką jest używanie for-each. Oprócz łamania zasady Keep It Simple, Stupid, new-fangledforEach() ma przynajmniej następujące braki:

  • Can ' t use non-final variables . Tak więc kod taki jak poniżej nie może być zamieniony na forEach lambda: {]}

    Object prev = null;
    for(Object curr : list)
    {
        if( prev != null )
            foo(prev, curr);
        prev = curr;
    }
    
  • Nie można obsługiwać zaznaczonych WYJĄTKÓW . Lambdy nie są w rzeczywistości zabronione rzucanie sprawdzonych wyjątków, ale wspólne funkcjonalne interfejsy takie jak Consumer nie deklarują żadnych. Dlatego każdy kod, który wyrzuca zaznaczone wyjątki, musi zawijać je w try-catch lub Throwables.propagate(). Ale nawet jeśli to zrobisz, nie zawsze jest jasne, co dzieje się z wyrzuconym wyjątkiem. Może zostać połknięty gdzieś w bebechach forEach()

  • Ograniczona kontrola przepływu . A return w lambdzie równa się a continue W for-each, ale nie ma odpowiednika a break. Trudno jest również wykonać takie czynności, jak zwracanie wartości, zwarcie lub ustawienie flags (co mogłoby nieco złagodzić sytuację, gdyby nie było naruszeniem reguły no non-final variables). " nie jest to tylko optymalizacja, ale krytyczne, jeśli weźmiemy pod uwagę, że niektóre sekwencje (takie jak czytanie linii w pliku) mogą mieć skutki uboczne lub możesz mieć nieskończoną sekwencję."

  • Może działać równolegle , co jest straszną, straszną rzeczą dla wszystkich, ale 0.1% kodu, który musi zostać zoptymalizowany. Dowolna równoległość kod musi być przemyślany (nawet jeśli nie używa zamków, ulotek i innych szczególnie nieprzyjemnych aspektów tradycyjnego wielowątkowego wykonywania). Każdy błąd będzie trudny do znalezienia.

  • Może zaszkodzić wydajności , ponieważ JIT nie może zoptymalizować forEach () + lambda w takim samym stopniu jak zwykłe pętle, zwłaszcza teraz, gdy lambda są nowe. Przez "optymalizację" nie mam na myśli narzutu wywołania lambda (który jest mały), ale do wyrafinowanej analizy i transformacji że nowoczesny kompilator JIT wykonuje na uruchomionym kodzie.

  • Jeśli potrzebujesz równoległości, prawdopodobnie korzystanie z ExecutorService jest znacznie szybsze i nie jest dużo trudniejsze. Strumienie są zarówno automagiczne (Czytaj: Nie wiem zbyt wiele o Twoim problemie) , jak i używają wyspecjalizowanej (Czytaj: nieefektywnej dla ogólnego przypadku) strategii paralelizacji (Fork-join rekurencyjny rozkład).

  • Sprawia, że debugowanie jest bardziej mylące , ponieważ zagnieżdżonej hierarchii połączeń i, broń Boże, równoległego wykonywania. Debugger może mieć problemy z wyświetlaniem zmiennych z otaczającego kodu, A takie czynności jak krok po kroku mogą nie działać zgodnie z oczekiwaniami.

  • Ogólnie Rzecz Biorąc, strumienie są trudniejsze do kodowania, odczytu i debugowania . W rzeczywistości dotyczy to ogólnie złożonych API "fluent". Kombinacja złożonych pojedynczych wyrażeń, intensywne stosowanie generyków i Brak zmiennych pośrednich mylące komunikaty o błędach i frustrujące debugowanie. Zamiast "ta metoda nie ma przeciążenia dla typu X" pojawia się komunikat o błędzie bliżej "gdzieś namieszałeś typy, ale nie wiemy gdzie i jak."Podobnie, nie można przejść i zbadać rzeczy w debuggerze tak łatwo, jak wtedy, gdy kod jest podzielony na wiele instrukcji, a wartości pośrednie są zapisywane w zmiennych. Wreszcie, czytanie kodu i zrozumienie typów i zachowań na każdym etapie wykonania może być nietrywialne.

  • Wystaje jak obolały kciuk . Język Java posiada już instrukcję for-each. Po co zastępować ją wywołaniem funkcji? Po co zachęcać do ukrywania efektów ubocznych gdzieś w wyrażeniach? Po co zachęcać nieporęcznych jednośladów? Mieszanie zwykłych for-each i nowych forEach jest złym stylem. Kod powinien mówić idiomami (wzory, które są szybkie do zrozumienia ze względu na ich powtarzalność), a im mniej idiomów jest używanych, tym wyraźniejszy jest kod i mniej czasu spędziłem decydując, którego idiomu użyć (Wielki drenaż czasu dla perfekcjonistów takich jak ja!).

Jak widzisz, nie jestem wielkim fanem forEach () z wyjątkiem przypadków, gdy ma to sens.

Szczególnie obraźliwy dla mnie jest fakt, że Stream nie implementuje Iterable (pomimo posiadania metody iterator) i nie może być używany w for-each, tylko z forEach(). Polecam przesyłanie strumieni do Iterabli za pomocą (Iterable<T>)stream::iterator. Lepsza alternatywa to użycie StreamEx który rozwiązuje wiele problemów z interfejsem Stream API, w tym implementację Iterable.

To powiedziane, forEach() jest przydatne dla:

  • Atomicznie iterację nad zsynchronizowaną listą . Wcześniej lista wygenerowana przez Collections.synchronizedList() była atomowa w odniesieniu do rzeczy takich jak get lub set, ale nie była bezpieczna dla wątków podczas iteracji.

  • Wykonanie równoległe (przy użyciu odpowiedniego strumienia równoległego) . Pozwala to zaoszczędzić kilka linijek kodu zamiast używania ExecutorService, jeśli twój problem pasuje do założeń wydajności wbudowanych w strumienie i Spliteratory.

  • Niektóre kontenery, takie jak lista zsynchronizowana, korzystają z kontroli nad iteracją (chociaż jest to w dużej mierze teoretyczne, chyba że ludzie mogą przywołać więcej przykładów)

    {31]}
  • Wywołanie pojedynczej funkcji bardziej czysto za pomocą forEach() i argumentu referencyjnego metody (ie, list.forEach (obj::someMethod)). Należy jednak pamiętać o punktach na zaznaczone wyjątki, trudniejsze debugowanie i zmniejszenie liczby idiomów używanych podczas pisania kodu.

Artykuły, których użyłem do odniesienia:

EDIT: wygląda jak niektóre z oryginalnych propozycji lambda (np. http://www.javac.info/closures-v06a.html ) rozwiązałem niektóre problemy, o których wspomniałem (oczywiście dodając własne komplikacje).

 401
Author: Aleksandr Dubinsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-12 07:32:04

Czytając to pytanie można odnieść wrażenie, że Iterable#forEach w połączeniu z wyrażeniami lambda jest skrótem / zamiennikiem zapisu tradycyjnej pętli for-each. To po prostu nieprawda. Ten kod z OP:

joins.forEach(join -> mIrc.join(mSession, join));

Jest Nie przeznaczone jako skrót do pisania

for (String join : joins) {
    mIrc.join(mSession, join);
}

I z pewnością nie powinny być używane w ten sposób. Jest to skrót do zapisu (choć jest to , a nie dokładnie to samo).]}

joins.forEach(new Consumer<T>() {
    @Override
    public void accept(T join) {
        mIrc.join(mSession, join);
    }
});

I jest jako zamiennik dla następującego kodu Java 7:

final Consumer<T> c = new Consumer<T>() {
    @Override
    public void accept(T join) {
        mIrc.join(mSession, join);
    }
};
for (T t : joins) {
    c.accept(t);
}

Zastąpienie ciała pętli interfejsem funkcjonalnym, jak w powyższych przykładach, czyni Twój kod bardziej wyraźnym: mówisz, że (1) ciało pętli nie wpływa na otaczający kod i przepływ sterowania, oraz (2) ciało pętli może być zastąpione inną implementacją funkcji, bez wpływu na otaczający kod. Brak dostępu do niekończących się zmiennych zakresu zewnętrznego nie jest deficytem z funkcji/lambda, jest to funkcja, która odróżnia semantykę Iterable#forEach od semantyki tradycyjnej pętli for-each. Gdy przyzwyczaisz się do składni Iterable#forEach, kod będzie bardziej czytelny, ponieważ natychmiast otrzymasz dodatkowe informacje o kodzie.

Tradycyjne pętle for-each z pewnością pozostaną dobrą praktyką (Aby uniknąć nadużywania terminu " najlepsza praktyka ") w Javie. Ale to nie znaczy, że Iterable#forEach należy rozważyć zła praktyka lub zły styl. Zawsze dobrą praktyką jest używanie odpowiedniego narzędzia do wykonywania pracy, a to obejmuje mieszanie tradycyjnych pętli for-each z Iterable#forEach, gdzie ma to sens.

Ponieważ wady Iterable#forEach zostały już omówione w tym wątku, oto kilka powodów, dla których prawdopodobnie będziesz chciał użyć Iterable#forEach: {]}

  • Aby uczynić Twój kod bardziej wyraźnym: jak opisano powyżej, Iterable#forEach Może sprawić, że Twój kod będzie bardziej wyraźny i czytelny w niektórych sytuacje.

  • Aby uczynić kod bardziej rozszerzalnym i możliwym do utrzymania: użycie funkcji jako ciała pętli pozwala zastąpić tę funkcję różnymi implementacjami(zobacz wzór strategii ). Można np. łatwo zastąpić wyrażenie lambda wywołaniem metody, które może być nadpisane przez podklasy:

    joins.forEach(getJoinStrategy());
    

    Wtedy można podać domyślne strategie używając enum, który implementuje funkcjonalny interfejs. To nie tylko sprawia, że Twoje kod jest bardziej rozszerzalny, zwiększa również konserwowalność, ponieważ oddziela implementację pętli od deklaracji pętli.

  • Aby Twój kod był bardziej debugowany: oddzielenie implementacji pętli od deklaracji może również ułatwić debugowanie, ponieważ możesz mieć wyspecjalizowaną implementację debugowania, która wypisuje komunikaty debugowania, bez konieczności zaśmiecania głównego kodu if(DEBUG)System.out.println(). Implementacja debugowania może być np. delegat , który dekoruje rzeczywistą implementację funkcji.

  • Aby zoptymalizować wydajność-krytyczny kod: wbrew niektórym twierdzeniom w tym wątku, Iterable#forEach czy już zapewnia lepszą wydajność niż tradycyjna pętla for-each, przynajmniej podczas korzystania z ArrayList i uruchamiania hotspotu w trybie "- client". Chociaż ten wzrost wydajności jest niewielki i znikomy w większości przypadków użycia, istnieją sytuacje, w których ta dodatkowa wydajność może coś zmienić. Np. opiekunowie bibliotek z pewnością będą chcieli ocenić, czy niektóre z ich istniejących implementacji pętli powinny zostać zastąpione Iterable#forEach.

    Aby poprzeć to stwierdzenie faktami, zrobiłem kilka mikro-benchmarków z Caliper {42]}. Oto kod testowy (potrzebny jest najnowszy Suwmiarka z git):

    @VmOptions("-server")
    public class Java8IterationBenchmarks {
    
        public static class TestObject {
            public int result;
        }
    
        public @Param({"100", "10000"}) int elementCount;
    
        ArrayList<TestObject> list;
        TestObject[] array;
    
        @BeforeExperiment
        public void setup(){
            list = new ArrayList<>(elementCount);
            for (int i = 0; i < elementCount; i++) {
                list.add(new TestObject());
            }
            array = list.toArray(new TestObject[list.size()]);
        }
    
        @Benchmark
        public void timeTraditionalForEach(int reps){
            for (int i = 0; i < reps; i++) {
                for (TestObject t : list) {
                    t.result++;
                }
            }
            return;
        }
    
        @Benchmark
        public void timeForEachAnonymousClass(int reps){
            for (int i = 0; i < reps; i++) {
                list.forEach(new Consumer<TestObject>() {
                    @Override
                    public void accept(TestObject t) {
                        t.result++;
                    }
                });
            }
            return;
        }
    
        @Benchmark
        public void timeForEachLambda(int reps){
            for (int i = 0; i < reps; i++) {
                list.forEach(t -> t.result++);
            }
            return;
        }
    
        @Benchmark
        public void timeForEachOverArray(int reps){
            for (int i = 0; i < reps; i++) {
                for (TestObject t : array) {
                    t.result++;
                }
            }
        }
    }
    

    A oto wyniki:

    Kiedy po uruchomieniu z"- client", Iterable#forEach przewyższa tradycyjną pętlę for nad tablicą, ale nadal jest wolniejsza niż bezpośrednia iteracja nad tablicą. Podczas uruchamiania z "- server " wydajność wszystkich podejść jest mniej więcej taka sama.

  • Aby zapewnić Opcjonalne wsparcie dla równoległego wykonywania: mówi się już tutaj, że możliwość wykonywania interfejsu funkcjonalnego Iterable#forEach równolegle przy użyciu strumieni , jest z pewnością ważnym aspektem. Od Collection#parallelStream() nie gwarantuje, że pętla jest faktycznie wykonywana równolegle, należy rozważyć tę funkcję opcjonalną. Poprzez iterację nad Twoją listą za pomocą list.parallelStream().forEach(...);, mówisz wprost: ta pętla obsługuje równoległe wykonywanie , ale nie zależy od niej. Ponownie, jest to cecha, a nie deficyt!

    Odsuwając decyzję o równoległym wykonaniu od rzeczywistej implementacji pętli, zezwalasz na opcjonalną optymalizację kodu, bez wpływu na kod siebie, co jest dobrą rzeczą. Ponadto, jeśli domyślna implementacja parallel stream nie odpowiada twoim potrzebom, nikt nie uniemożliwia ci dostarczenia własnej implementacji. Można np. zapewnić zoptymalizowaną kolekcję w zależności od bazowego systemu operacyjnego, rozmiaru kolekcji, liczby rdzeni i niektórych ustawień preferencji: {]}

    public abstract class MyOptimizedCollection<E> implements Collection<E>{
        private enum OperatingSystem{
            LINUX, WINDOWS, ANDROID
        }
        private OperatingSystem operatingSystem = OperatingSystem.WINDOWS;
        private int numberOfCores = Runtime.getRuntime().availableProcessors();
        private Collection<E> delegate;
    
        @Override
        public Stream<E> parallelStream() {
            if (!System.getProperty("parallelSupport").equals("true")) {
                return this.delegate.stream();
            }
            switch (operatingSystem) {
                case WINDOWS:
                    if (numberOfCores > 3 && delegate.size() > 10000) {
                        return this.delegate.parallelStream();
                    }else{
                        return this.delegate.stream();
                    }
                case LINUX:
                    return SomeVerySpecialStreamImplementation.stream(this.delegate.spliterator());
                case ANDROID:
                default:
                    return this.delegate.stream();
            }
        }
    }
    

    Fajną rzeczą jest to, że Twoja implementacja pętli nie musi wiedzieć ani troszczyć się o te szczegóły.

 94
Author: Balder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-13 16:58:46

forEach() może być zaimplementowany tak, aby był szybszy niż dla-każdej pętli, ponieważ iterable zna najlepszy sposób iteracji swoich elementów, w przeciwieństwie do standardowego sposobu iteracji. Różnica polega więc na pętli wewnętrznej lub pętli zewnętrznej.

Na przykład ArrayList.forEach(action) może być po prostu zaimplementowane jako

for(int i=0; i<size; i++)
    action.accept(elements[i])

W przeciwieństwie do pętli for-each, która wymaga dużej ilości rusztowań

Iterator iter = list.iterator();
while(iter.hasNext())
    Object next = iter.next();
    do something with `next`

Jednak musimy również uwzględnić dwa koszty ogólne używając forEach(), jeden z nich tworzy obiekt lambda, drugim jest wywołanie metody lambda. Prawdopodobnie nie są one znaczące.

Zobacz też http://journal.stuffwithstuff.com/2013/01/13/iteration-inside-and-out / do porównywania wewnętrznych / zewnętrznych iteracji dla różnych przypadków użycia.

 11
Author: ZhongYu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-11 16:01:45

TL; DR: List.stream().forEach() był najszybszy.

Czułem, że powinienem dodać moje wyniki z iteracji benchmarkingu. Przyjąłem bardzo proste podejście (bez ram benchmarkingowych) i benchmarked 5 różnych metod:

  1. classic for
  2. classic foreach
  3. List.forEach()
  4. List.stream().forEach()
  5. List.parallelStream().forEach

Procedura badania i parametry

private List<Integer> list;
private final int size = 1_000_000;

public MyClass(){
    list = new ArrayList<>();
    Random rand = new Random();
    for (int i = 0; i < size; ++i) {
        list.add(rand.nextInt(size * 50));
    }    
}
private void doIt(Integer i) {
    i *= 2; //so it won't get JITed out
}

Lista w tej klasie powinna być iterowana i mieć pewne doIt(Integer i) stosowane do wszystkich to członkowie, za każdym razem inną metodą. w klasie głównej uruchamiam testowaną metodę trzy razy, aby rozgrzać JVM. Następnie uruchamiam metodę testową 1000 razy sumując czas potrzebny dla każdej metody iteracji(używając System.nanoTime()). Po tym wszystkim dzielę tę sumę przez 1000 i to jest wynik, średni czas. przykład:

myClass.fored();
myClass.fored();
myClass.fored();
for (int i = 0; i < reps; ++i) {
    begin = System.nanoTime();
    myClass.fored();
    end = System.nanoTime();
    nanoSum += end - begin;
}
System.out.println(nanoSum / reps);
W 2008 roku firma została założona przez firmę java.]}

Classic for

for(int i = 0, l = list.size(); i < l; ++i) {
    doIt(list.get(i));
}

Czas wykonania: 4.21 ms

Classic foreach

for(Integer i : list) {
    doIt(i);
}

Czas wykonania: 5.95 ms

List.forEach()

list.forEach((i) -> doIt(i));

Czas wykonania: 3.11 ms

List.stream().forEach()

list.stream().forEach((i) -> doIt(i));

Czas wykonania: 2.79 ms

List.parallelStream().forEach

list.parallelStream().forEach((i) -> doIt(i));

Czas wykonania: 3.6 ms

 8
Author: Assaf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-25 05:16:03

Czuję, że muszę nieco rozszerzyć mój komentarz...

o paradygmacie \ style

To chyba najbardziej godny uwagi aspekt. FP stał się popularny ze względu na to, co można uzyskać unikanie skutków ubocznych. Nie będę zagłębiał się głęboko w to, jakie plusy\minusy można uzyskać z tego, ponieważ nie jest to związane z pytaniem.

Powiem jednak, że iteracja za pomocą Iterable.forEach jest inspirowany FP i raczej wynikiem wprowadzenia większej liczby FP do Javy (jak na ironię powiedziałbym, że nie ma wiele zastosowań dla forEach w czystym FP, ponieważ nie robi nic poza wprowadzaniem efektów ubocznych).

Na koniec powiedziałbym, że jest to raczej kwestia gustu \ stylu \ paradygmatu, w którym aktualnie piszesz.

o paralelizmie.

Z punktu widzenia wydajności nie ma obiecanych znaczących korzyści z używania Iterable.forEach over foreach (...).

Według oficjalnych dokumentów na Iterable.forEach :

Wykonuje daną akcję na zawartość Iterable, W elementy porządkowe występują podczas iteracji, dopóki wszystkie elementy nie zostaną przetwarzane lub akcja wyrzuca wyjątek.

... tj. docs całkiem jasne, że nie będzie ukrytej paralelizmu. Dodanie jednego byłoby naruszeniem LSP.

Teraz są" parallell collections", które są obiecane w Javie 8, ale aby pracować z tymi, musisz mi bardziej wyraźnie i poświęcić trochę uwagi, aby ich używać (patrz odpowiedź mschenk74 dla przykład).

BTW: w tym przypadku Stream.forEach zostanie użyty i nie gwarantuje, że rzeczywista praca zostanie wykonana w parallell (zależy od Bazowej kolekcji).

Aktualizacja: może nie być tak oczywiste i trochę rozciągnięty na pierwszy rzut oka, ale jest inny aspekt stylu i perspektywy czytelności.

Po pierwsze-zwykłe stare forloopy są zwykłe i stare. Wszyscy już je znają.

Po drugie i ważniejsze - pewnie chcesz użyj Iterable.forEach tylko z jednowarstwowymi lambdami. Jeśli" ciało " staje się cięższe-wydają się nie być-że czytelne. Masz tutaj 2 opcje-użyj klas wewnętrznych (yuck) lub użyj zwykłego starego forloopa. Ludzie często denerwują się, gdy widzą, że te same rzeczy (iteracje nad kolekcjami) są wykonywane w różnych vays/styles w tej samej bazie kodowej, i tak się wydaje.

Ponownie, może to być problem, ale nie może być problemem. Zależy od ludzi pracujących nad kodem.

 3
Author: Eugene Loy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-19 18:59:55

Jednym z najbardziej upleasing funkcjonalnych ograniczeń forEach jest brak obsługi sprawdzonych WYJĄTKÓW.

Jeden możliwe obejście polega na zastąpieniu terminala forEach zwykłą starą pętlą foreach:

    Stream<String> stream = Stream.of("", "1", "2", "3").filter(s -> !s.isEmpty());
    Iterable<String> iterable = stream::iterator;
    for (String s : iterable) {
        fileWriter.append(s);
    }

Oto lista najpopularniejszych pytań z innymi obejściami dotyczącymi obsługi sprawdzonych WYJĄTKÓW w lambdzie i strumieniach:

Java 8 Lambda funkcja rzucająca wyjątek?

Java 8: Lambda-strumienie, Filtrowanie metodą z Exception

Jak mogę wyrzucać zaznaczone wyjątki ze strumieni Java 8?

Java 8: obowiązkowe sprawdzanie WYJĄTKÓW w wyrażeniach lambda. Dlaczego obowiązkowe, a nie opcjonalne?

 2
Author: Vadzim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:26

Zaletą metody Java 1.8 forEach nad 1.7 Enhanced for loop jest to, że podczas pisania kodu można skupić się tylko na logice biznesowej.

Metoda ForEach wykorzystuje Javę.util.funkcja.Consumer object jako argument, więc pomaga mieć naszą logikę biznesową w oddzielnej Lokalizacji, że można go ponownie użyć w każdej chwili.

Spójrz na poniższy fragment,

  • Tutaj stworzyłem nową klasę, która nadpisze metodę accept class z klasy konsumenckiej, gdzie możesz dodać dodatkową funkcjonalność, więcej niż iterację..!!!!!!

    class MyConsumer implements Consumer<Integer>{
    
        @Override
        public void accept(Integer o) {
            System.out.println("Here you can also add your business logic that will work with Iteration and you can reuse it."+o);
        }
    }
    
    public class ForEachConsumer {
    
        public static void main(String[] args) {
    
            // Creating simple ArrayList.
            ArrayList<Integer> aList = new ArrayList<>();
            for(int i=1;i<=10;i++) aList.add(i);
    
            //Calling forEach with customized Iterator.
            MyConsumer consumer = new MyConsumer();
            aList.forEach(consumer);
    
    
            // Using Lambda Expression for Consumer. (Functional Interface) 
            Consumer<Integer> lambda = (Integer o) ->{
                System.out.println("Using Lambda Expression to iterate and do something else(BI).. "+o);
            };
            aList.forEach(lambda);
    
            // Using Anonymous Inner Class.
            aList.forEach(new Consumer<Integer>(){
                @Override
                public void accept(Integer o) {
                    System.out.println("Calling with Anonymous Inner Class "+o);
                }
            });
        }
    }
    
 1
Author: Hardik Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-23 14:59:03