• Archiv der Kategorie: Lambda

Reihen generiern mit Stream-API

public class LerneUnendlicheReihen {
    public static void main(String[] args) {
        IntStream reihe = IntStream.iterate(100, i -> ++i);
        reihe.limit(100).forEach(System.out::println);

        /**
         * Output:
         * 100
         * 101
         * ...usw...
         * 199
         */

        "abcdefg".chars().forEach(System.out::println);

        System.out.println("Nun mit Integer Objekten:");
        AtomicInteger atomicInteger = new AtomicInteger(100);
        Stream<Integer> objektReihe = Stream.generate(atomicInteger::getAndIncrement);
        objektReihe.limit(10).forEach(System.out::println);

        /* Ausgabe:
         Nun mit Integer Objekten:
         100
         101
         * ...usw...
         109
         */

    }
}

Stream Reduce anwenden, um Auswertungen über Elemente zu machen

import static org.assertj.core.api.Assertions.assertThat;

public class LerneReduce {
    public static void main(String[] args) {
        List<String> bros = TestDataProvider.getBrothersInArms();

        assertThat(
            bros.stream().reduce((a, b) -> a + ", " + b).get()
        ).isEqualTo("Walther, Hans, Erich, Paul");

        IntStream oneTo3 = IntStream.iterate(1, i -> ++i).limit(3);
        assertThat(
             oneTo3.reduce((i1, i2) -> i1 + i2).getAsInt()
        ).isEqualTo(6);

    }
}

Comparator – Schnelles Erstellen

Comparator personaComparator 
= Comparator.<strong>comparing</strong>(Persona::getAge).<strong>thenComparing</strong>(Persona::getName))
import static org.assertj.core.api.Assertions.assertThat;

public class LerneComparator {
    final static Persona JONA = new Persona(7, "Jona");
    final static Persona ZORA = new Persona(11, "Zora");
    final static Persona NATASHA = new Persona(22, "Natasha");
    final static Persona[] personas = {JONA, ZORA, NATASHA};

    public static void main(String[] args) {

        List&lt;Persona&gt; personList = Arrays.asList(personas);

        System.out.println("Mit Comparator:");
        Collections.sort(personList, Comparator.comparing(Persona::getAge).thenComparing(Persona::getName));
        assertThat(personList).containsExactly(JONA, ZORA, NATASHA);

        System.out.println("Mit Comparator (reverse order):");
        Collections.sort(personList, Comparator.comparing(Persona::getAge).thenComparing(Persona::getName).reversed());
        assertThat(personList).containsExactly(NATASHA, ZORA, JONA);

        System.out.println("Mit Comparable Objects: ");
        Collections.sort(personList); //Persona must implement Comparable-Interface
        assertThat(personList).containsExactly(JONA, NATASHA, ZORA);
    }

}

Kumulierbare/verknüpfbare Kriterien erstellen mit Hilfe von Functional interfaces/Lambda (Übung) / Fluent Interface / Method Chaining

Java 8 – Lösung:

public interface Specification<T> {

    default Specification<T> and(Specification spec){
        Specification<T> outerSpec = this;
        return obj -> outerSpec.isSatisfiedBy(obj) && spec.isSatisfiedBy(obj);
    }
    boolean isSatisfiedBy(T obj);
}

Erklärung:

Eine Spezifikation definiert (in isSatisfiedBy(T obj)) wann sie erfüllt ist.

Die and(..) Methode gibt eine Kombination aus der vorliegenden (this) und der übergebenen Spezifikation zurück.

Aufruf:

import static org.assertj.core.api.Assertions.assertThat;

public class Test {
    public static void main(String[] args) {
        Specification<String> istHans = s -> s.equals("Hans");
        Specification<String> laengeNichtNull = s -> !s.isEmpty();
        Specification<String> kombinierteSpezifikation = istHans.and(laengeNichtNull);

        assertThat(kombinierteSpezifikation.isSatisfiedBy("Hans"))
                .as("Kombinierte Spezifikation trifft zu").isTrue();
        assertThat(kombinierteSpezifikation.isSatisfiedBy("Peter"))
                .as("Kombinierte Spezifikation trifft nicht auf 'Peter' zu").isFalse();
        assertThat(laengeNichtNull.isSatisfiedBy("Peter"))
                .as("Nur die eine Spezifikation (nicht leer) trifft zu").isTrue();


        /**
         * Spezifikation kann auch anders parametrisiert werden:
         */
        Specification<Integer> groesser7 = i -> i.compareTo(7) > 0;
        Specification<Integer> istGerade = i -> (i & 1) == 0;

        assertThat(groesser7.and(istGerade).isSatisfiedBy(8)).isTrue();
        assertThat(groesser7.and(istGerade).isSatisfiedBy(9)).isFalse();
        assertThat(groesser7.and(istGerade).isSatisfiedBy(6)).isFalse();
    }
}
 Begriffe:

Fluent API: Wenn Aufrufe aneinander gekettet werden können, dass dabei eine natürlich verständliche Aussage wiedergegeben wird, z.B.:

assertThat(laengeNichtNull.isSatisfiedBy("Peter"))
        .as("Nur die eine Spezifikation (nicht leer) trifft zu").isTrue();

Method Chaining: Verkettung von Methoden-Aufrufen (wie eben gezeigtes Beispiel). Dabei wird oft ein Object derselben Klasse wir das Objekt auf der die Methode aufgerufen wird zurück gegeben:

public class Klasse {
    ...
    //Method that allows chaining
    Klasse addName(String s){
        ...
    }
}

Zum Vergleich eine Version ohne Java 8 Fetures:

public interface Specification<T> {

    Specification<T> and(Specification spec);

    boolean isSatisfiedBy(T obj);

}

Da Default-Methoden als Sprachmittel fehlen wird die Methode and(..) in einer abstrakten Klasse implementiert:

public abstract class SpecificationImpl<T> implements Specification<T> {

    public Specification<T> and(Specification spec){
        Specification<T> outerSpec = this;
        return new SpecificationImpl<T>() {
            @Override
            public boolean isSatisfiedBy(T obj) {
                return outerSpec.isSatisfiedBy(obj) && spec.isSatisfiedBy(obj);
            }
        };
    }
}

Aufruf:
Die konkreten Spezifikationen müssen (z.B. als anonyme) Klassen ausprogrammiert werden:

public class Test {
    public static void main(String[] args) {
        SpecificationImpl<String> istHans = new SpecificationImpl<String>() {
            @Override
            public boolean isSatisfiedBy(String s) {
                return s.equals("Hans");
            }
        };
        SpecificationImpl<String> laengeNichtNull = new SpecificationImpl<String>() {
            @Override
            public boolean isSatisfiedBy(String s) {
                return !s.isEmpty();
            }
        };
        Specification<String> kombinierteSpezifikation = istHans.and(laengeNichtNull);

        assertThat(kombinierteSpezifikation.isSatisfiedBy("Hans"))
                .as("Kombinierte Spezifikation trifft zu").isTrue();
        assertThat(kombinierteSpezifikation.isSatisfiedBy("Peter"))
                .as("Kombinierte Spezifikation trifft nicht auf 'Peter' zu").isFalse();
        assertThat(laengeNichtNull.isSatisfiedBy("Peter"))
                .as("Nur die eine Spezifikation (nicht leer) trifft zu").isTrue();


        /**
         * Spezifikation kann auch anders parametrisiert werden:
         */
        SpecificationImpl<Integer> groesser7 = new SpecificationImpl<Integer>() {
            @Override
            public boolean isSatisfiedBy(Integer i) {
                return i.compareTo(7) > 0;
            }
        };
        Specification<Integer> istGerade = new SpecificationImpl<Integer>() {
            @Override
            public boolean isSatisfiedBy(Integer i) {
                return (i & 1) == 0;
            }
        };

        assertThat(groesser7.and(istGerade).isSatisfiedBy(8)).isTrue();
        assertThat(groesser7.and(istGerade).isSatisfiedBy(9)).isFalse();
        assertThat(groesser7.and(istGerade).isSatisfiedBy(6)).isFalse();
    }
}

Zum Vergleich nochmals:

< Java 8: Java 8: Lambda
SpecificationImpl<String> laengeNichtNull = new SpecificationImpl<String>() {
    @Override
    public boolean isSatisfiedBy(String s) {
        return !s.isEmpty();
    }
};
Specification<String> istHans = s -> s.equals("Hans");

Lambda mit mehreren parametern – Comparator

new Comparator<HealthCheckPosition>(){
    @Override
    public int compare(HealthCheckPosition o1, HealthCheckPosition o2) {
        return DateTimeComparator.getDateOnlyInstance().compare(o1.getInstrument().getRelevantExpirationDate(), o2.getInstrument().getRelevantExpirationDate());
    }
};
   

Als Lambda:

(Comparator<HealthCheckPosition>) (o1, o2) -> DateTimeComparator.getDateOnlyInstance().compare(o1.getInstrument().getRelevantExpirationDate(), o2.getInstrument().getRelevantExpirationDate());

Mittels Stream API Flache Liste von Liste von Listen erstellen

Beispiel:

List<List<Object>> list = ...
List<Object> flat = 
    list.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());

(taken from)

 

Erklärung:

// List of Lists:
List<List<String>> listOfLists = new ArrayList<>();
listOfLists.add(Arrays.asList("Buenos Aires", "Córdoba", "La Plata"));
listOfLists.add(Arrays.asList("Bern", "Chur", "Luzern"));

List<String> result = listOfLists.stream().flatMap(list -> list.stream()).collect(Collectors.toList());

Signatur von flatMap(..):

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

T : Das Element mit dem der Stream parametrisiert ist, auf dem flatMap(..) aufgerufen wird.

 

Von flatMap verlanges Functional interface:
Function<? super T, ? extends Stream<? extends R>> :

interface Function<T, R> {
   R apply(T t);
}

Das heisst bezüglich übergebener Funktion/Lambda-Expression:

Input von apply: Ist gleich dem Element-Parameter des Streams auf dem flatMap aufgerufen wird.

Output von apply: <? extends Stream<? extends R>>:
Ein Stream von irgendwas. –> Ich darf selbst bestimmen, was R dann wirklich ist. Dadurch, wie meine übergebene Funktion aktuell parametrisiert ist, wird R erst bestimmt.

    private List getAllAepfel(Map<UID, List<Apfel>> uidToApfelList) {
        List xq = uidToApfelList.values().stream().flatMap(List::stream).collect(Collectors.toList());
    }
Schließen