<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://mletkin.net/index.php?action=history&amp;feed=atom&amp;title=Streams%3A_Ergebnis_sammeln</id>
	<title>Streams: Ergebnis sammeln - Versionsgeschichte</title>
	<link rel="self" type="application/atom+xml" href="https://mletkin.net/index.php?action=history&amp;feed=atom&amp;title=Streams%3A_Ergebnis_sammeln"/>
	<link rel="alternate" type="text/html" href="https://mletkin.net/index.php?title=Streams:_Ergebnis_sammeln&amp;action=history"/>
	<updated>2026-05-06T13:55:45Z</updated>
	<subtitle>Versionsgeschichte dieser Seite in MimiPedia</subtitle>
	<generator>MediaWiki 1.39.4</generator>
	<entry>
		<id>https://mletkin.net/index.php?title=Streams:_Ergebnis_sammeln&amp;diff=109&amp;oldid=prev</id>
		<title>Ullrich: Die Seite wurde neu angelegt: „Category:Java Genau wie die hier beschriebene {{java|reduce}}-Methode, dient die {{java|collect}}-Methode des {{java|Stream}}-…“</title>
		<link rel="alternate" type="text/html" href="https://mletkin.net/index.php?title=Streams:_Ergebnis_sammeln&amp;diff=109&amp;oldid=prev"/>
		<updated>2023-03-09T17:04:09Z</updated>

		<summary type="html">&lt;p&gt;Die Seite wurde neu angelegt: „&lt;a href=&quot;/index.php?title=Kategorie:Java&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;Kategorie:Java (Seite nicht vorhanden)&quot;&gt;Category:Java&lt;/a&gt; Genau wie die &lt;a href=&quot;/index.php?title=Streams:_Daten_einkochen&quot; title=&quot;Streams: Daten einkochen&quot;&gt;hier beschriebene&lt;/a&gt; {{java|reduce}}-Methode, dient die {{java|collect}}-Methode des {{java|Stream}}-…“&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Neue Seite&lt;/b&gt;&lt;/p&gt;&lt;div&gt;[[Category:Java]]&lt;br /&gt;
Genau wie die [[Streams: Daten einkochen|hier beschriebene]] {{java|reduce}}-Methode,&lt;br /&gt;
dient die {{java|collect}}-Methode des {{java|Stream}}-Interface dem Einkochen des Ergebnisses auf&lt;br /&gt;
einen einzelnen Wert. Betrachtet man eine Liste von Objekten selbst als Objekt, so kann man das Sammeln der&lt;br /&gt;
Ergebnis-Objekte, die die Verarbeitung durch den Stream überlebt haben und schließlich aus dem Abfluß tropfen&lt;br /&gt;
als Spezialfall davon betrachten. Tatsächlich sind beide Konstrukte&lt;br /&gt;
({{java|reduce()}} und {{java|collect}}) Sichtweisen auf den gleichen Mechanismus und nachdem wir&lt;br /&gt;
[[Streams: Daten einkochen|hier]] reduziert haben, kollektivieren wir nun.&lt;br /&gt;
&lt;br /&gt;
Das Sammeln übernimmt ein passendes Objekt das das Interface {{java|Collector}} implementiert. Die&lt;br /&gt;
Klasse {{java|Collectors}} (man beachte das Plural-s) stellt über statische Methoden nützliche Implementierungen zur&lt;br /&gt;
Verfügung von denen einige im Folgenden kurz vorgestellt werden sollen. Im Anschluß betrachten wir, wie mit der {{java|collect}}-Methode&lt;br /&gt;
beliebige Datenstrukturen befüllt werden können.&lt;br /&gt;
&lt;br /&gt;
== Listen und Mengen ==&lt;br /&gt;
Um die Ergebnis-Objekte in ein {{java|List}}-Objekt zu füllen, liefert die – recht intuitive – Methode&lt;br /&gt;
{{java|Collectors.toList()}} einen passenden Collector:&lt;br /&gt;
{{java|code=List&amp;lt;String&amp;gt; zahlen = Arrays.stream(&amp;quot;eins&amp;quot;, &amp;quot;zwei&amp;quot;, &amp;quot;drei&amp;quot;).collect(Collectors.toList());}}&lt;br /&gt;
damit werden die drei Strings aus dem Array in eine Liste überführt. So läßt sich aber auch eine Methode schreiben,&lt;br /&gt;
die eine Liste beliebigen Typs in eine neue Liste kopiert:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public static &amp;lt;T&amp;gt; List&amp;lt;T&amp;gt; copy(List&amp;lt;T&amp;gt; quelle) {&lt;br /&gt;
    return quelle.stream().collect(Collectors.toList());&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
Analog dazu liefert die Methode {{java|Collectors.toSet()}} einen Collector, der einen {{java|Set}} des entsprechenden Typs erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Zeichenketten ==&lt;br /&gt;
Möchten wir eine Liste von Strings anstelle zu einer Liste zu einem (einzigen) String zusammenführen,&lt;br /&gt;
haben wir gleich drei Möglichkeiten dafür einen Collector zu erzeugen:&lt;br /&gt;
;joining()&lt;br /&gt;
:klebt einfach die Strings aneinander&lt;br /&gt;
:Aus {{java|Arrays.stream(&amp;quot;1&amp;quot;, &amp;quot;2&amp;quot;, &amp;quot;3&amp;quot;).collect(Collectors.joining())}} ensteht so der String {{java|&amp;quot;123&amp;quot;}}&lt;br /&gt;
;joining(delimiter)&lt;br /&gt;
:fügt zwischen zwei Strings den angegebenen &amp;quot;delimiter&amp;quot; ein.&lt;br /&gt;
:Aus {{java|Arrays.stream(&amp;quot;1&amp;quot;, &amp;quot;2&amp;quot;, &amp;quot;3&amp;quot;).collect(Collectors.joining(&amp;quot;,&amp;quot;))}} ensteht so der String {{java|&amp;quot;1,2,3&amp;quot;}}&lt;br /&gt;
;joining(delimiter, prefix, suffix)&lt;br /&gt;
:erweitert die Liste um eine Anfangs- und eine Ende-Markierung.&lt;br /&gt;
:Aus {{java|Arrays.stream(&amp;quot;1&amp;quot;, &amp;quot;2&amp;quot;, &amp;quot;3&amp;quot;).collect(Collectors.joining(&amp;quot;,&amp;quot;, &amp;quot;[&amp;quot;, &amp;quot;]&amp;quot;))}} entsteht so der String {{java|&amp;quot;[1,2,3]&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
== Maps ==&lt;br /&gt;
Mit der Methode {{java|Collectors.toMap()}} läßt sich der Objekt-Strom auch in eine (Hash-)Map abfüllen.&lt;br /&gt;
Da ein Map-Eintrag aus zwei Teilen, nämlich Key und Value, besteht, sind hier zusätzliche Angaben erforderlich;&lt;br /&gt;
nämlich wie Key und Value aus dem Objekt berechnet werden. Das geschieht – Überraschung – mithilfe von {{lambda}}-Ausdrücken.&lt;br /&gt;
Betrachten wir folgende Klasse, die jeweils einen Kunden und einen Artikel zusammenfaßt und eine Bestellung darstellen möge:&lt;br /&gt;
{{java|code=&lt;br /&gt;
class Bestellungen {&lt;br /&gt;
    private String kdnr;&lt;br /&gt;
    private String artNr;&lt;br /&gt;
    public String getKdnr() { return kdnr; }&lt;br /&gt;
    public String getArtNr() { return artNr; }&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
Wir möchten eine Map generieren, die jeder Kundennummer die Nummer des Artikels zuweist, den der Kunde&lt;br /&gt;
bestellt hat. Der Key errechnet sich daher direkt aus der Kundennummer:&lt;br /&gt;
{{java|code=(Bestellung b) -&amp;gt; b.getKdnr()}}&lt;br /&gt;
und der Value aus der Artikel-Nummer: &lt;br /&gt;
{{java|code=(Bestellung b) -&amp;gt; b.getArtNr()}}&lt;br /&gt;
Eine Bestellungen-Liste läßt sich dann mit der Methode {{java|Collectors.toMap()}} in eine String-String-Map umwandeln:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public Map&amp;lt;String, String&amp;gt; mapping(List&amp;lt;Bestellung&amp;gt; liste) {&lt;br /&gt;
    return liste.stream().collect(Collectors.toMap(&lt;br /&gt;
        (Bestellung b) -&amp;gt; b.getKdnr(),&lt;br /&gt;
        (Bestellung b) -&amp;gt; b.getArtNr()&lt;br /&gt;
    ));&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
Oder – kompakter geschrieben – so:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public Map&amp;lt;String, String&amp;gt; mapping(List&amp;lt;Bestellung&amp;gt; liste) {&lt;br /&gt;
    return liste.stream().collect(Collectors.toMap(&lt;br /&gt;
        Bestellung::getKdnr,&lt;br /&gt;
        Bestellung::getArtNr&lt;br /&gt;
    ));&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
Die {{java|toMap}}-Methode hat allerdings eine recht unschöne Eigenart. Da sie die {{java|merge}}-Methode von&lt;br /&gt;
{{java|HashMap}} verwendet, läßt sie keine {{java|null}}-Werte zu. Wenn man also nicht sicher weiß, daß der&lt;br /&gt;
value-Ausdruck niemals {{java|null}} liefert, fällt man früher oder später der Null-Pointer-Exception zum Opfer.&lt;br /&gt;
Alternativ kann man die Map von Hand erzeugen und statt mit {{java|collect()}} die Objekte mit {{java|forEach()}}&lt;br /&gt;
der Map hinzufügen:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public Map&amp;lt;String, String&amp;gt; mapping(List&amp;lt;Bestellung&amp;gt; liste) {&lt;br /&gt;
     Map&amp;lt;String, String&amp;gt; result = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
     liste.stream().forEach(best -&amp;gt; result.put(best.getKdnr(), best.getArtNr()));&lt;br /&gt;
     return result;&lt;br /&gt;
 }&lt;br /&gt;
}}&lt;br /&gt;
Oder man wählt die Variante, die als letztes vorgestellt werden soll:&lt;br /&gt;
&lt;br /&gt;
== Eigene Datenstrukturen befüllen ==&lt;br /&gt;
Die letzte Variante kann als Verallgemeinerung der vorangegangenen Varianten auf beliebige Datenstrukturen verstanden werden. Neben der Umschiffung des genannten map-Problems kann man sie auch verwenden wenn man eine selbstgebaute Datenstruktur mit der {{java|collect}}-Methode aus einem Stream befüllen möchte. Dazu werden drei Angaben in Form von {{lambda}}-Ausdrücken benötigt:&lt;br /&gt;
* Ein Erzeuger für eine Datenstruktur&lt;br /&gt;
* Eine Anweisung die der Datenstruktur ein Objekt hinzuzufügt&lt;br /&gt;
* Eine Anweisung die zwei Datenstrukturen zusammengeführt&lt;br /&gt;
Daß ein Erzeuger für die Datenstruktur benötigt wird ist klar, daß eine Möglichkeit benötigt wird, ein neues Objekt&lt;br /&gt;
in die Datenstruktur einzuordnen ist auch klar, aber wozu dient die dritte Anweisung – die im JDK-Source als&lt;br /&gt;
&amp;quot;combiner&amp;quot; bezeichnet wird?&lt;br /&gt;
&lt;br /&gt;
Java-Streams sind darauf ausgelegt, in einer nebenläufigen Umgebung parallelisiert verarbeitet zu werden. Das&lt;br /&gt;
bedeutet technisch, daß der Stream in viele kleine Streams aufgeteilt wird die – jeder für sich – einen Teil des&lt;br /&gt;
Gesamt-Streams verarbeiten. Nach getaner Arbeit müssen die einzelnen Teilergebnisse zusammengeführt werden,&lt;br /&gt;
dabei enstehen im Falle von {{java|collect}} mehrere Datenstrukturen (Strings, List-Objekte oder was auch immer) die am&lt;br /&gt;
Ende zu einer einzigen Datenstruktur zusammengefaßt werden müssen. Dazu dient der combiner, der zwei&lt;br /&gt;
Datenstrukturen zusammenfaßt und am Ende solange auf die Teilergebnisse angewandt wird bis nur noch eine&lt;br /&gt;
einzige große Sammlung da ist.&lt;br /&gt;
Als Beispiel erzeugen wir die HashMap aus dem vorangegangenen Abschnitt nun mit der {{java|collect}}-Methode.&lt;br /&gt;
#Als Erzeuger dient {{java|new}}. Verlangt wird dafür ein {{lambda}}-Ausdruck der keine Parameter hat und eine HashMap liefert:&lt;br /&gt;
#:{{java|() -&amp;gt; new HashMap&amp;lt;String, String&amp;gt;()}}&lt;br /&gt;
#Die Objekte werden der HashMap mit {{java|put()}} hinzugefügt. Parameter sind hier zunächst die HashMap und dann das Objekt mit den hinzuzufügenden Daten:&lt;br /&gt;
#:{{java|(map, bestellung) -&amp;gt; map.put(bestellung.getKdnr(), bestellung.getArtNr())}}&lt;br /&gt;
#Zum Zusammenführen bietet das {{java|Map}}-Interface die Methode {{java|putAll()}} an:&lt;br /&gt;
#:{{java|(map1, map2) -&amp;gt; map1.putAll(map2))}}&lt;br /&gt;
die vollständige Methode zur Erzeugung der Map sieht dann so aus:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public Map&amp;lt;String, String&amp;gt; mapping(List&amp;lt;Bestellung&amp;gt; liste) {&lt;br /&gt;
    return liste.stream().collect(&lt;br /&gt;
        () -&amp;gt; new HashMap&amp;lt;String, String&amp;gt;(),&lt;br /&gt;
        (map, bestellung) -&amp;gt; map.put(bestellung.getKdnr(), bestellung.getArtNr()),&lt;br /&gt;
        (map1, map2) -&amp;gt; map1.putAll(map2)&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
Mit Verwendung der {{java|::}}-Notation wird das Ganze noch etwas übersichtlicher und wir überlassen es dem Compiler,&lt;br /&gt;
die richtigen Datentypen für die Hash-Map zu ermitteln:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public Map&amp;lt;String, String&amp;gt; mapping(List&amp;lt;Bestellung&amp;gt; liste) {&lt;br /&gt;
    return liste.stream().collect(&lt;br /&gt;
        HashMap::new,&lt;br /&gt;
        (map, bestellung) -&amp;gt; map.put(bestellung.getKdnr(), bestellung.getArtNr()),&lt;br /&gt;
        HashMap::putAll&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
Wem das noch nicht genügt, der kann das {{java|Collector}}-Interface selbst implementieren und einen eigenen Collector&lt;br /&gt;
damit bauen. Aber das ist eine andere Geschichte, die ein anderes Mal erzählt werden soll. Die {{java|Collectors}}-Klasse&lt;br /&gt;
hat noch weitere Methoden für parallele Verarbeitung von Maps, Gruppierung, Summierung und ähnliches – auch&lt;br /&gt;
das wird ein andermal erzählt werden.&lt;/div&gt;</summary>
		<author><name>Ullrich</name></author>
	</entry>
</feed>