Java 8 Streams
Optional
Traditionally variables can either have a value or be null. Controlling if any variable is null or not is a major problem in Java. Optionals are here to help with this. If it is expected that a value can be null you might better want to use an Optional for it. With an Optional you can not access the value without thinking about the case that it is not there. In this example the parameter maybe is expected to be not filled.
// 1a
String result=maybe.map(str -> str+".").orElseGet(() -> getAlternative());
// 1b orElse() needs to have its parameter evaluated while orElseGet only when its called
String value=maybe.orElse("Not there!");
// 2a
maybe.ifPresent(s -> {
System.out.println(s);
});
// 2b
maybe.ifPresent(System.out::println);
// bad example, you can and will forget the if test
if(maybe.isPresent()) {
String value=maybe.get();
System.out.println(value);
}
}
And here are 3 ways to generate an Optional value. Either the Optional is empty or it has an non null value. The of() method check you not create empty values.
opt2 = Optional.ofNullable(objectWhichIsAllowedToBeNull);
opt3 = Optional.empty(); // content is null
Interface mit default
Bisher konnten Interfaces nur abstrakte Methoden beinhaltet. Ab jetzt können Interfaces statische Methoden und sogenannte Default Methoden beinhalten. Wenn man die nicht überschreibt, erbt man die samt Funktionalität.
// abstract method (nothing new)
public abstract T doSomething();
// a non abstract method in an interface
default String getStandardResult() {
return "New!";
}
// a non abstract static method, can not be overwritten in client
public static String getNoResult() {
return "New and fixed";
}
}
FunctionalInterface
Ein sogenanntes FunctionalInterface ist auf den ersten Blick ein normales Interfaces, welches aber genau eine abstrakte Methode hat
public interface MyFunctionalInterface {
public abstract Integer apply(String parameter);
}
Interessant sind sie, weil man ihnen die neuen Lambda Ausdrücke zuweisen kann.
Man kann sogar Generics dafür benutzen.
public interface MyFunctionalInterface<V> {
public abstract V mergeVales(V v1, V v2);
}
Jetzt kann eine Method ein Lamda als Parameter erwarten, dass 2 Werte vom Typ V erwartet und eines vom Typ V zurückliefert:
userFunction.mergeValues(x.get(0), x.get(1) );
}
Lambda Ausdrücke
Ein Lambda Ausdruck ist wie eine kompakt zu schreibende Funktion ohne Namen
Sogar die Parametertypen sind optional
Eclipse kann beim Speichern automatisch Lambda Ausdrücke im Code ersetzen (Vorsicht, das sind großflächige Änderungen)
Nachdem der Lambda Ausdruck dem FunctionalInterface zugewiesen wurde, kann man ihn aufrufen, indem man die Methode des Interfaces aufruft
Es gibt zahlreiche vordefinierte Functional Interfaces
In den Lambda Ausdrücken kann man auch auf den lokelen Scope zugreifen, lokale Variables müssen dabei final sein (oder es zumindest sein können)
Lambda Ausdruck mit zwei Parametern
Praktische Anwendung, Sortieren von Strings nach Länge
Arrays.sort(myArrayString, (String s1, String s2) -> s2.length() - s1.length());
Functional Interface that takes a parameter but returns nothing
Functional Interface that does not expect a parameter but returns something
Function Interface that expects one parameter and returns a boolean
Warum das Lambda als Comperator akzeptiert wird.
Streams
Weil Streams die Daten erst anziehen, wenn sie auch gebraucht werden, sollte man sie statt Collections verwenden. Z.B. ist es nicht notwendig alle Zahlen in eine Collection schreiben zu lassen, wenn der User am Ende nur die erste Zahl sucht, die größer als 42 ist. Möglicherweise trifft das für die erste Zahl im Stream schon zu, dann wird der Rest erst gar nicht verarbeitet, belegt keinen Speicher, muss nicht aus einer Datenquelle geladen werde, usw. Nachteil am Stream ist allerdings dass sich die Daten in der Quelle noch ändern können. If you consume the stream completely you might be disappointed by the performance loose their overhead brings with them, even if you consume them in parallel.
Create a Stream
Create a stream from a hard codes list of elements
Create a stream from a range of values
Create a stream from an endless working supplier
private int currentValue = 0;
@Override
public Integer get() {
return currentValue += 2;
}
}
Stream.generate(new MyEndlessSupplier()).limit(10);
A more practical example for this (endless stream of random numbers)
If the Stream should end by itself base it on an Iterator / Iterable
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
private int currentValue = 0;
@Override
public boolean hasNext() {
return currentValue <= 10;
}
@Override
public Integer next() {
return currentValue++;
}
};
}
}
StreamSupport.stream(new MyEndingSupplier().spliterator(), false).forEach(System.err::println);
Create a Stream from the lines of a file
Files.lines(Paths.get(url.toURI())).filter(...);
Create a stream out of an Array
myStream=Arrays.stream(myArray);
From a Collection
myStream=myList.stream();
Ein Stream erzeugen der parallel (mehrere Threads) abgearbeitet wird
Ein Stream kann dabei ordered oder nicht ordered sein. Hat z.B. auf ein distinct() auf einem parallelen Stream Auswirkungen. Parallelism Sofern einem die Reihenfolge der Streamverarbeitung nicht wichtig ist, erhält man so Parallelverarbeitung die die ursprüngliche Reihenfolge ignoriert.
Aus mehreren Streams einen Stream machen
String[] arr2 = { "e", "f", "g" };
String[] arr3 = { "h", "i", "j" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
Stream<String> stream3 = Stream.of(arr3);
Stream<String> stream = Stream.concat(Stream.concat(stream1, stream2), stream3);
Oder so
Find Filtering Reordering
Mit
kann man die ersten x Einträge überspringen.
Filter elements that fulfill condition
myStream().filter(s -> s!=null);
myStream().filter(Objects::nonNull);
myStream().filter(String.class::isInstance);
Non stream alternative
Is there a value in the Stream that fits, do all fit, did non fit?
e = myStream.anyMatch((i) -> (i>42) );
e = myStream.allMatch((i) -> (i>42) );
e = myStream.noneMatch((i) -> (i>42) );
Give me the first element, give me any element
Optional<Person> x=p.stream().findAny();
Distinct (each value only once)
Is slower for parallel ordered stream as it might be important which one to keep
Sortieren
myPeopleStream.sorted(personByAge).forEach(e -> System.out.println(e));
Map values to something else
Jedes Element des Stream an eine Methode zur Verarbeitung geben. Die Methode muss nichts zurückliefern, die Elemente verbleiben im Stream. Die Methode kann aber natürlich die Elemente inhaltlich verändern.
Einen Wert auf einen anderen abbilden
Oder String auf Integer
Wenn man aus einem Wert auch mehrere oder keinen Wert erzeugen können will, dann geht das mit flatMap, welches pro Wert einen neuen Stream hinzufügt. Beispielsweise für jede Zahl im Stream auch ihr doppeltes hinzufügen
Class Cast in Stream, first filter on type than cast final Integer i = 42; final Optional<Number> n = Optional.of(i).filter(Number.class::isInstance).map(Number.class::cast);
Consume stream
Count number of values in it
Durch den Stream durchlaufen
Werte zusammenfassen, Summe aller Lebensjahre, -1 falls Stream leer
Höchstes Alter
If the reduced value is of a different type than the values we reduce we need to explain how to create the reduced value, how to add another value to it and how to reduce to values into one
// identity value
new MyContainer(),
// accumulator, called all the time
(col, data) -> col.add(data),
// combiner, called to collect values of parallel executions
(col1, col2) -> col1.addAll(col2)
);
Mit collect kann man einen Stream beenden und die Resultate zurückliefern. collect erwartet als Parameter die Information wie die Daten aufgesammelt werden sollen.
Der erste Parameter erzeugt eine neue Instanz des Sammlers (hier ein neues Foo Objekt mit dem Default Konstruktor). Der zweite Parameter wird mit jedem neuen Objekt aus dem Stream aufgerufen. Foo kann dann irgendwas mit dem Objekt machen. Der dritte Parameter wird mit einem anderen Objekt aus dem Stream aufgerufen und wir müssen seinen Inhalt mit uns verschmelzen. Am Ende liefern wir genau ein Foo Objekt zurück, das alle anderen in sich vereinigt hat.
Collectors hat eine Reihe nützlicher Funktionen. Aus dem Stream eine Liste erzeugen
Aus dem Stream eine Map erzeugen, Geschlecht -> alle Namen
Durchschnittlichen Wert aus Stream ermitteln
Den höchsten Wert mithilfe eines anzugebenden Comperators finden
Statistiken erhalten
Elemente gruppieren, hier alle Personen nach Geschlecht gruppieren
Element aufteilen, in alle die, die eine Bedingung erfüllen, und alle die sie nicht erfüllen. Z.B. ist Person über 30 oder nicht