Wie verwende ich Java Streams bei zwei For Loops?

Streams sind wirklich eine feine Sache. So fein, dass ich heutzutage praktisch keine For-Loops mehr sehe. Streams sind auch tatsächlich einfach und am Ende sauschnell, um die Daten zu verarbeiten.

Aber wie verwende ich Streams, wenn ich zwei For Loops benötigen würde für die Verarbeitung? Also wenn ich eine „Liste in einer anderen Liste“ habe?

Die kurze Antwort ist:

  • Wenn man beim Verarbeiten der inneren Liste auf die Objekte der äusseren Liste zugreifen will, verwendet man zwei Streams() mit AnyMatch.
  • Wenn man beim Verarbeiten der inneren Liste nicht auf die Objekte der äusseren Liste zugreifen will, kann man FlatMap verwenden.

FlatMap? Was ist das schon wieder für eine Teufelei?

Ja schrecklich, nicht? Da denkst Du, Du hättest endlich mal alles wissenswerte über Java gelernt, und dann fange ich an, von FlatMaps zu schwafeln.

Um FlatMaps zu erklären fange ich gleich an mit dem Beispiel, dass ich auch unten im Code zeigen werden:

Ich habe eine Liste von Müttern und jede Mutter hat wiederum eine Liste von Ihren Kindern. Meine Aufgabe ist es, die Mutter mit dem Kind „Paul“ zu finden.

Die Daten könnten also folgendermassen aussehen:

Äussere Liste:

  • Mutter Janet
  • Mutter Petra

Janet hat zwei Kinder:

  • John
  • Jessica

Petra hat zwei Kinder:

  • Patrick
  • Paul

Wenn ich jetzt eine FlatMap verwende, erstelle ich für die Mütter einen Stream und erhalte dann einfach alle Kinder aneinandergereiht statt „pro Mutter“

Also statt

Janet { John, Jessica } Petra { Patrick, Paul }

erhalte ich

{ John, Jessica, Patrick, Paul }

Wenn ich jetzt die Information der Mutter nicht benötige und nur die Attribute eines Kindes prüfen kann (Beispielsweise: „Ist das Kind älter als 6 Jahre?“) dann funktioniert eine FlatMap sehr gut.

Sobald ich aber die Information der dazugehörigen Mutter brauche, geht das nicht mehr (Beispiel: „Zu welcher Mutter gehört das Kind Paul?“). Dann kann ich zwei Streams verwenden (siehe Codebeispiel unten).

Okay ich bin bereit. Bring mir den Code!

Der Code zeigt zuerst, wie man für Paul auf herkömmliche Weise herausfindet, wer seine Mutter ist. Dann verwende ich zwei Streams mit anyMatch im inneren Stream. Und am Ende noch eine FlatMap, wobei ich aber fälschlicherweise am Ende das Kind erhalte statt die Mutter.

Die Klassen für Mutter und Kind sowie das setzen der Werte finden sich ganz unten in der Klasse.

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class FindPaul {

List<Mother> mothers = new ArrayList<>();

public void findPaul() {
	setupMothers();
	twoForLoops();
	twoStreams();
	flatMap();	
}

------------------------------------------------------------

private void twoForLoops() {
	for(Mother  mother: mothers) {
		for (Child child: mother.children) {
			if("Paul".equals(child.name)) {
				System.out.println("1: " + mother.name + " is the mother of Paul");
			}
		}
	}
}

------------------------------------------------------------

private void twoStreams() {
	Optional<Mother> paulsMother = mothers.stream().filter(
		mother -> mother.children.stream().anyMatch(child -> "Paul".equals(child.name))				
	).findFirst();				
	System.out.println("2: " + paulsMother.get().name + " is the mother of Paul");		
}

------------------------------------------------------------

// flatMap makes the conversion: 
// Stream<List<Object>>	-> flatMap ->	Stream<Object>
private void flatMap() {
	// Fehler: Mit Flatmap hat man keinen Bezug mehr auf das "Parent" Element
	// Man kriegt ein "Child", nicht die Mutter
	Optional<Child> paulsMother = mothers.stream().flatMap(mother -> mother.children.stream()).filter(child -> "Paul".equals(child.name)).findFirst();					
	System.out.println("3: " + paulsMother.get().name + " is Paul itself, and who is the mother?");
}

------------------------------------------------------------

private void setupMothers() {
	Mother m1 = new Mother("Janet");
	m1.children.add(new Child("John"));
	m1.children.add(new Child("Jessica"));
	
	Mother m2 = new Mother("Petra");
	m2.children.add(new Child("Patrick"));
	m2.children.add(new Child("Paul"));
	
	mothers.add(m1);
	mothers.add(m2);
}

class Child {
	String name;		
	Child(String name) { this.name = name; }
}

class Mother {
	String name;
	List<Child> children = new ArrayList<>();
	Mother(String name) { this.name = name; }
}	
}

Übrigens: Falls jemand eine noch bessere Methode hat, um zwei For Loops zu verarbeiten, bin ich jederzeit bereit, etwas dazuzulernen. 🙂

AnyMatch und AllMatch

Noch ein kurzer Nachtrag: Wenn man zwei For Loops durch Streams ersetzt und dabei die Lösung verwendet, bei der man in der inneren Schleife die äusseren Objekte kennen muss (oben im Code die Methode twoStreams()) dann muss man noch aufpassen, dass man tatsächlich das richtige herausfiltert.

Bei dem angegebenen Beispiel mit „AnyMatch“ muss im inneren Stream einfach ein Kind „Paul“ heissen.

Will man aber, dass alle Kinder „Paul“ heissen, muss man statt AnyMatch „AllMatch“ verwenden. Der Rest des Codes bleibt der gleiche.

Du kannst in deinem Java 8 Projekt keine JAR Datei verwenden die in einer höheren Version kompiliert wurde

Was wie das elfte Gebot klingt ist mir tatsächlich passiert: Ich habe an meinem Java 8 Projekt gearbeitet und musste dann eine Model-Klasse von einem anderen Projekt verwenden. Doof nur: Das andere Projekt ist bereits in Java 11 programmiert (noch ohne Module zu verwenden).

Ich habe mir nichts dabei gedacht, sondern wie gewohnt die Maven Abhängigkeit im Pom.xml eingetragen und kurz darauf habe ich die Jar-Datei in meinem Projekt in IntelliJ gefunden.

Als ich die Klasse versucht habe, zu verwenden, war IntelliJ sogar noch höflich zu mir und hat mir gesagt „Ja gut, ich kenne diese Klasse, die liegt doch in diesem Jar-Dings, du kannst sie also einfach importieren„.

Als ich die Klasse aber tatsächlich importiert habe kam nur die Meldung „Cannot resolve symbol„.

Ich habe alles mögliche versucht: Den Cache in IntelliJ invalidiert+restartet, den Target Ordner gelöscht und nochmals Maven:clean-install gemacht, und so weiter, aber nichts hat geholfen. IntelliJ konnte die Klasse einfach nicht finden. Selbst im Classpath war die Klasse beziehungsweise das Jar vorhanden.

Ganz zufällig habe ich etwas entdeckt, als ich die Klasse dekompiliert betrachtet habe: IntelliJ hat mich darauf hingewiesen, dass die Klasse in Java 11 kompiliert wurde. Das habe ich zu Beginn nicht gewusst, obwohl ich in diesem Blogpost natürlich explizit über genau diese Situation schreibe.

Was auch mühsam war: Bei meiner Internetrecherche bin ich auf niemanden getroffen, der die gleiche Situation beschrieben hat. Sobald man irgendwas wie „Use class from java 11 in java 8 project“ bei Google sucht kommen alles nur Anleitungen, wie man von Java 8 auf Java 11 migriert.

Weder die Internetrecherche noch meine Arbeitskollegen konnten mir weiterhelfen. Was also tun? Natürlich! Frage auf Stackoverflow gepostet und prompt hatte ich 8 Minuten später ganze 3 Antworten, die es mir erklärten:

You cannot use a jar file compiled in higher version of java in a project developed in lower version of java

Da momentan viele Firmen noch Java 8 verwenden und gegebenfalls zum Teil bereits auf Java 11 gewechselt haben ist es sicherlich praktisch, wenn jeder Java-Entwickler diesen Satz zumindest mal gehört hat.

Was ist die Corda Blockchain?

Achtung: Corda ist momentan brandheiss! Wäre Corda eine heisse Suppe, wären momentan viele Menschen bereit, sich die Zunge zu verbrennen, um mehr Informationen darüber zu erhalten oder entsprechende Projekte umzusetzen.

Corda ist eine Open Source Blockchain Plattform basierend auf der Distributed Ledger Technology (DLT). Corda gehört zur dritten Blockchain Welle.

Eine private Blockchain

Weiss eigentlich mittlerweile jeder, was eine Blockchain ist? Falls nicht, bitte kurz die Einführung dazu lesen.

Corda ist eine Weiterentwicklung der ursprünglichen Blockchain für Bitcoins. Der Grundgedanke ist aber geblieben: In einem Netzwerk hat es verschiedene Teilnehmer (Englisch: peers oder nodes oder participants) und jeder Teilnehmer hat eine Buchhaltung über die getätigten Transaktionen. (Ich mag das Wort Buchhaltung mehr als Hauptbuch, obwohl das eigentlich treffender wäre für das englische Wort ledger. Schlussendlich könnte man auch Datenbank sagen, das ist schlussendlich nicht so relevant für das Verständnis).

Im Gegensatz zur Bitcoin Blockchain hat aber jeder Teilnehmer seine eigene Buchhaltung!

Es gilt nicht mehr das Prinzip, dass alle Transaktionen öffentlich sind und im gesamten Netz verteilt werden. Teilnehmer A kann Teilnehmer B Geld schicken, und nur diese beiden Nodes kennen dann diese Transaktionen.

Und besonders im Businessbereich, etwa bei Banken, ist die Privatsphäre das oberste Gut. Und Corda verspricht, diese Privatsphäre zu gewähren und zu schützen.

Zusammenarbeit mit Industriepartnern

Der grosse Vorteil von Corda: Es wurde von Grund auf für das Business konzipiert. Die dafür zuständige Firma R3 steht dazu im Kontakt mit über 200 Mitgliedern der Industrie, darunter Banken oder Wirtschaftsverbände.

Das Ziel von Corda ist es, komplexe Finanztransaktionen durchführen zu können. Der Austausch von Bitcoin zwischen A und B ist aus finanztechnischer Sicht nämlich eine einfache Transaktion. In einer Bank oder einer Versicherung müssen hunderte weitere Finanzinstrumente umgesetzt werden können.

Und wo werden die umgesetzt? Natürlich in Code, und dafür gibt es uns Programmierer 🙂

Die Programmierlogik wird dabei in sogenannte CorDapps gepflanzt.

Obwohl Corda von der traditionellen Blockchain inspiriert wurde, gibt es darin keine Kryptowährung. Corda geht noch viel weiter: Das Ziel ist es, jedes beliebige Finanzinstrument definieren und verwenden zu können.

Double Spending Problem wird durch Notare verhindert

Ein grosses Problem bei Blockchain Netzwerken ist das Double Spending Problem. In Corda sind die Lösung dazu sogeannte Notare. Dabei handelt es sich um einen weiteren Teilnehmer im Corda Blockchain Netzwerk – Dieser Teilnehmer kann aber mehrere Notare beinhalten.

Notare haben das Ziel, zu prüfen, dass bereits ausgegebenes Geld nicht noch einmal ausgegeben wird. Das wird technisch relativ einfach implementiert: Alle Transaktionen beziehungsweise jeder Geldbetrag wird in einer Liste gespeichert. Wenn der Geldbetrag schon einmal ausgegeben wurde, kann er nicht noch einmal ausgegeben werden.

Ja gut, das ist jetzt vielleicht ein bisschen arg fest vereinfacht erklärt (obwohl es natürlich Sinn macht). Für technische Details mache ich eventuell mal einen eigenen Blogeintrag.

Ein kleiner technischer Einblick in Corda

In Corda gibt es besonders drei technische Objekte, die von zentraler Relevanz sind:

  • States (welche in Transaktionen verwendet werden)
  • Contracts
  • Flows

Ein State ist dabei quasi ein Snapshot von einem beliebigen Objekt – etwa eines Kontos. Eine Transaktion ist dann die Überführung von einem Zustand (Input State) zu einem anderen (Output State). Wenn Person A also 100 Dollar an B zahlen will, wird eine Transaktion erstellt, bei der Person A im Input State 100 Dollar besitzt und im Output State diese an Person B übergeben werden.

Technisch isch ein State einfach eine Java… ähm ne warte, eine Kotlin Klasse mit Attributen, Gettern und Settern. Es können aber beliebige Attribute definiert werden, und genauso können beliebige finanzielle Produkte in Code erstellt werden.

Ein Contract beinhaltet Prüfungen, die eine Transaktion bestehen muss, damit diese valid ist. Wenn Person A 100 Dollar an Person B geben will, kann eine solche Prüfung etwa sein „Hat Person A 100 Dollar auf dem Konto?“ oder auch „Hat Person A überhaupt ein Konto bei uns?„. Die Prüfungen können beliebig eincodiert werden.

Ein Flow beschreibt den gesamten Transaktionsfluss. Wenn Person A Geld an Person B schicken will, muss Person A beziehungweise sein Node in der Blockchain eine Transaktion erzeugen und diese mit dem entsprechenden Contract prüfen lassen. Dann schickt Person A die Transaktion an Person B/Node B und B prüft die Transaktion ebenfalls. Erst wenn beide Teilnehmer die Transaktion erfolgreich validieren, wird diese ausgeführt und committed. Dieser Prozess wird in Corda als Flow implementiert.

Das folgende Bild verdeutlicht das Konzept des Flows. Generell muss ich sagen, dass das Lehrmaterial ausgezeichent ist (siehe Kapitel „Corda lernen“ unten) und mit vielen Bildern/Videos unterstützt wird.

Beispiel eines Flows

Das Bild kommt von dieser URL: https://docs.corda.net/key-concepts-flows.html

Kryptografische Highlights

Aus Sicht eines Kryptografens werden nicht allzu verrückte Techniken eingesetzt.

Ein wichtiger Teil sind Hashfunktionen. Sie wissen schon, dass sind diese Dinger, wo man irgendetwas reinquetschen kann und heraus kommt ein einzigartier Hash wie etwa der folgende:
7e716d0e702df0505fc72e2b89467910

Es wird so ziemlich alles verhasht, was man sich vorstellen kann. Die Transaktionen in der Buchführung eines Nodes werden natürlich auch miteinander verlinkt und unveränderbar (immutable) gemacht, ganz dem Konzept von Blockchains. Jede Transaktion erhält dann den Hash der vorhergehenden Transaktion – und nicht die Transaktion selber.

Das zweite grosse Konzept sind digitale Signaturen – Genau wie in anderen Blockchains. Wenn Person A Geld an Person B schicken will, muss A seine Transaktion signieren, damit B prüfen kann, dass die Transaktion wirklich von A kommt. Genauso wird B die Transaktion signieren, wenn diese erfolgreich validiert werden konnte.

Corda lernen

Corda ist in Kotlin geschrieben. Wer also Corda lernen möchte, der hat damit gleichzeitig eine schöne Motivation, um Kotlin zu lernen.

Ansonsten empfehlen ich jedem, der Corda lernen möchte, folgendes:

Für alle Personen empfehle ich das wirklich gut gemachte Webinar von R3, welches die Corda Konzepte erläutert:
https://r3.lessonly.com/path/5150-corda-key-concepts

Natürlich ist auch die offizielle Homepage und Referenz sehr empfehlenswert:
https://docs.corda.net/index.html

Auf der Corda Homepage kann ein Zertifikation über Corda erworben werden: https://www.corda.net/develop/index.html

Und Entwicklern, die ihre eigenen CorDapps entwickeln dürfen, sei das Bootcamp auf Youtube empfohlen:

Der Link mit der gesamten Playlist ist folgender:
https://www.youtube.com/playlist?list=PLi1PppB3-YrVq5Qy_RM9Qidq0eh-nL11N

Corda auf Stackoverflow (corda tag):
https://stackoverflow.com/questions/tagged/corda