Die Verwendung streams ab java8 erlaubt schöne Konstruktionen zwecks Abfrage von Collections und Mappen.
Eine schöne Übung für das Verständnis der Verwendung dieser ist die Umwandlung von SQL-Abfragen in streams. An diesem Beitrag möchte ich ein paar dieser Umwandlungen vorstellen.
Das Beispiel – Die Datendefinition
Zunächst einmal brauchte ich für den zugrundeliegenden Stream eine Liste von Objekten.
- in java ist eine Liste von Objekten
- in SQL ist eine Tabelle.
Also implementierte ich in java eine Klasse. Als Beispiel nahm ich einen Mitarbeiter.
class Mitarbeiter{
private static int anzahl = 0;
int ma_id;
String nachname;
double gehalt;
String abteilung;
public Mitarbeiter(String nachname, double gehalt, String abteilung){
this.ma_id = ++anzahl;
this.nachname = nachname;
this.gehalt = gehalt;
this.abteilung = abteilung;
}
public String toString(){
return ma_id + "|" + nachname + "|" + gehalt + "|" + abteilung;
}
}
Eine entsprechende Tabelle in MySQL habe ich mit einem auto_increment für die Mitarbeiternummer (ma_id) aufgebaut; daher wird dies entsprechend hier in java auch so umgesetzt.

Als Nächstes brauchte ich noch ein paar Testdaten:
- in MySQL: Datensätze in der Tabelle
- in java: eine Liste mit Objekten der Klasse Mitarbeiter
Hier für java8 die Definition der Liste:
Stream tabelle = Arrays.asList(
new Mitarbeiter("schulze", 1200, "buha"),
new Mitarbeiter("meier", 1350, "prod"),
new Mitarbeiter("müller", 1400, "buha"),
new Mitarbeiter("richard", 1150, "prod")
).stream();
In MySQL sieht die Tabelle entsprechend wie folgt aus:

Die Abfragen
Und hier nun eine Reihe von Abfragen, die ich in java auf dem stream als auch als auch in Sql ausgeführt habe:
Ausgabe aller Mitarbeiter
Es werden alle Eigenschaften eines jeden Mitarbeiters ausgegeben. In SQL bedeutet dies, es müssen alle Felder aus der Tabelle ausgeben werden. In java8 gibt es die Funktion forEach(), welche als Iterator über alle Objekte dient. Diese Funktion verlangt als Typ einen Consumer; hier wird die Funktion println verwendet, um das aktuelle Objekt im stream auszugeben. Für die Ausgabe aller Eigenschaften eines Objektes wurde die Funktion toString() aus der Oberklasse Object bei der Definition der Klasse überschrieben.
java 8 | tabelle.forEach(System.out::println); |
MySQL | select * from mitarbeiter |
Ausgabe alphabetisch sortiert nach Nachname
Hier sollen ebenfalls alle Datensätze bzw. Objekte ausgegeben werden. Zusätzlich soll VOR der Ausgabe eine aufsteigende Sortierung durchgeführt werden. In SQL wird dies über die Angabe von order by, gefolgt von einem Feld realisiert. In java8 handelt es sich um eine Verkettung von 2 Aktionen. Zunächst muss die Liste mit den Objekten sortiert werden. Dazu wird die Funktion sorted verwendet, welche als Parameter einen Wert vom Typ Comparator erwartet. Anschließend erfolgt die Ausgabe aller Objekte über die Iteration mittels der Funktion forEach.
java 8 | tabelle.sorted((m1,m2)-> m1.nachname.compareTo(m2.nachname)).forEach(System.out::println); |
MySQL | select * from mitarbeiter order by nachname |
Ausgabe der Anzahl aller Mitarbeiter
Hier soll ein Wert ausgegeben werden. Die Anzahl der Zeilen in der Tabelle bzw. die Anzahl der Objekte in der Liste muss gezählt werden. Das wird mit der Funktion count() realisiert.
java 8 | System.out.println(tabelle.count()); |
MySQL | select count(*) from mitarbeiter |
Ausgabe Summe aller Gehälter
Auch hier soll nur eine Zahl ausgegeben werden. In beiden Fällen wird final die Funktion sum() verwendet. In SQL wird die Summe aller Werte aus der Spalte gehalt bestimmt. In java8 muss zunächst eine Konvertierung (ein Mapping) einer Liste von Objekten in eine Liste von double-Werten erfolgen. (Da wir von unseren Objekten nur jeweils die Eigenschaft gehalt benötigen). Anschließend kann über alle Werte dieser Liste die Summe gebildet werden.
java 8 | System.out.println(tabelle.mapToDouble(m -> m.gehalt).sum()); |
MySQL | select sum(gehalt) from mitarbeiter |
Ausgabe durchschnittliches Gehalt
Dabei handelt es sich nur um eine kleine Abwandlung der Summen-Ausgabe. In SQL verwende ich hier die Funktion avg(), welcher ich die Spalte gehalt übergebe. In java8 muss hier zunächst wieder ein Mapping von einer Liste von Mitarbeitern zu einer Liste von Zahlen vornehmen. Anschließend kann ich den Durchschnitt berechnen lassen. Doch Obacht: es kann sein, dass die Liste leer ist, dann gibt es keinen Durchschnitt. Die Funktion average() gibt ein Optional zurück: entweder es gibt einen Wert oder nicht. Nur, wenn es einen Wert gibt, kann dieser auch ausgegeben werden.
java 8 | tabelle.mapToDouble(m -> m.gehalt).average().ifPresent(System.out::println); |
MySQL | select avg(gehalt) from mitarbeiter |
Ausgabe aller Mitarbeiter, deren Gehalt größer 1200 ist
Die Datensätze bzw. Mitarbeiterobjekte müssen hier gefiltert werden für die Ausgabe. In SQL wird diese Einschränkung durch die Verwendung von where realisiert. In java8 gibt es eine Funktion filter(), welche ein Predicat verlangt. Dieses entspricht einem Vergleich. Die Funktion filter() beschränkt die Liste auf alle Objekte, deren Werte dem Predicat entsprechen und können anschließend direkt ausgegeben werden.
java 8 | tabelle.filter(m -> m.gehalt > 1200).forEach(System.out::println); |
MySQL | select * from mitarbeiter where gehalt > 1200; |
Ausgabe des zweiten und dritten Datensatzes
Auch hierbei handelt es sich um einen Filter. Dieser orientiert sich jedoch nicht an den Eigenschaften, sondern an der Position des Objektes in der Liste bzw. des Datensatzes in der Tabelle. Wenn nur der 2. und 3. Datensatz ausgegeben werden soll, dann kann man dies wie folgt übersetzen: überspringe den 1. Datensatz (skip) und verwende die nächsten beiden (Limit).
java 8 | tabelle.skip(1).limit(2).forEach(System.out::println); |
MySQL | select * from mitarbeiter limit 1,2 |
Ausgabe des dritten Datensatzes bei absteigender Sortierung nach Gehalt
Das entspricht dem vorherigen Bespiel, nur dass hier 2 Datensätze übersprungen werden (skip) und dann einer ausgegeben (Limit). Bevor man das macht, muss allerdings eine absteigende Sortierung erfolgen. Dazu wird wieder die Funktion sorted verwendet, welche als Parameter einen Vergleich erwartet.
java 8 | tabelle.sorted((m1,m2)-> ((Double)m2.gehalt).compareTo(m1.gehalt)).skip(2).limit(1).forEach(System.out::println); |
MySQL | select * from mitarbeiter order by gehalt desc limit 2,1; |
Ausgabe aller Mitarbeiter, bei Nummer als MA_
in SQL besteht die Möglichkeit, Werte von Zellen zu verbinden. Dazu wird concat() verwendet. In java8 gibt es etwas ähnliches. Dazu muss die Ausgabe eines kompletten Datensatzes angepasst werden. Die Funktion map() leistet diese Dienste.
java 8 | tabelle.map(m -> “MA_”+m.ma_id+”|”+m.nachname+”|”+m.gehalt+”|”+m.abteilung).forEach(System.out::println); |
MySQL | select concat(“MA_”,ma_id), nachname, gehalt, abteilung from mitarbeiter |
Gruppierung der Mitarbeiter nach Abteilung
Mit der Funktion collect() kann ich Objekte einer Liste anhand eines Kriteriums sammeln bzw. sortieren.
java 8 | System.out.println(tabelle.collect(Collectors.groupingBy(m -> m.abteilung))); |
MySQL | select abteilung, count(*) from mitarbeiter group by abteilung; |
Hinweis zum letzten Beispiel:
Hier enstprechen sich die Ausgaben nicht. In java8 erhalte ich eine Mappe mit den keys buha und prod und den dazugehörigen Objekten. In SQL hingegen erhalte ich nur die Aggregation der Abteilung als Anzahl der zugehörigen Datensätze. Hier ist java8 wohl überlegen \m/