Texte aufsplitten und zusammenführen in Java 8 – Teil 2

Nachdem wir uns die verschiedenen Split-Varianten des JDK angeschaut haben, wollen wir in diesem Teil der kleinen Blog-Serie schauen, wie man Datenstrukturen zu Strings zusammenführen kann. Vor Java 8 gab es dazu keine spezielle Funktionalität, man musste dieses Verhalten vielmehr selbst implementieren. Das hat dazu geführt, dass Bibliotheken wie Apache Commons, Google Guava oder die Programmiersprache Groovy eigene Implementierungen angeboten haben.

Mit Java 8 haben wir nun sogar mehrere Varianten. Beispielsweise gibt es direkt auf der String-Klasse eine Methode join, die als ersten Parameter ein Trennzeichen und dann entweder eine Collection oder eine variable Argumentliste von einzelnen zu verkettenden Bestandteilen erwartet. Das ermöglicht uns mit einem Einzeiler eine Liste in eine Zeichenkette umzuwandeln.

@Test
public void testStringJoin() {
	assertEquals("a, b, c", String.join(", ", Arrays.asList("a", "b", "c")));
	assertEquals("a, b, c", String.join(", ", "a", "b", "c"));
}

Zusätzlich gibt es noch eine Klasse StringJoiner, von der man sich weitere Konfigurationsmöglichkeiten erwarten würde. Zum Vergleich kann der Guava-Joiner beispielsweise mit Null-Werten umgehen, Leerzeichen abschneiden und so weiter. Leider erfüllt sich diese Erwartung nicht, vielmehr macht der JDK-StringJoiner das Zusammenführen nur unnötig komplizierter.

@Test
public void testStringJoinerForLoop() {
	// kein Join auf einer Liste, immer nur mit add einzelne Elemente hinzufügbar
	StringJoiner joiner = new StringJoiner(", ");
	for (String str : Arrays.asList("a", "b", "c")) {
		joiner.add(str);
	}
	assertEquals("a, b, c", joiner.toString());
}

Man muss explizit über eine Datenstruktur iterieren und für jedes Element StringJoiner.add() aufrufen. Genaugenommen verwenden wir den StringJoiner also eher analog zum StringBuilder/-Buffer, haben also scheinbar keinen direkten Mehrwert. Aber das stimmt nicht ganz. Dem StringJoiner kann man nämlich noch ein Präfix und ein Suffix mitgeben, welche automatisch an den Anfang bzw. das Ende des Ergebnisses angehängt werden.

@Test
public void testStringJoinerSingleAdds() {
	// StringJoiner sehr ähnlich zu StringBuilder
	assertEquals("a, b, c", new StringJoiner(", ").add("a").add("b").add("c").toString());
	assertEquals("a b c", new StringJoiner(" ").add("a").add("b").add("c").toString());
	assertEquals("[a,b,c]", new StringJoiner(",", "[", "]").add("a").add("b").add("c").toString());
}

Durch die forEach()-Methode und die Übergabe von add() als Methoden-Referenz (higher order function) kann man die Verwendung des StringJoiners zwar wieder vereinfachen, für viele wird dieses Syntaxkonstrukt aber noch gewöhnungsbedürftig sein.

@Test
public void testStringJoinerForEachAdd() {
	StringJoiner joiner = new StringJoiner(", ");
	Arrays.asList("a", "b", "c").forEach(joiner::add);
	assertEquals("a, b, c", joiner.toString());
}

Stephen Colebourne empfiehlt auf seinem Blog:

“The Java SE 8 StringJoiner class is in my opinion nothing more than a behind-the-scenes tool. It should only be used indirectly from String.join() or Collectors.joining().”

Letzteres fügt sich wieder prima in die neue Welt der Streams ein. Im letzten Blog-Beitrag haben wir uns ja genau ein solches Beispiel für das Gegenstück, das Aufsplitten, angeschaut.

@Test
public void testCollectorsJoining() {
	assertEquals("a, b, c", Arrays.asList("a", "b", "c").stream().collect(Collectors.joining(", ")));
	assertEquals("1, 2, 3", Arrays.asList(1, 2, 3).stream().map(Object::toString).collect(Collectors.joining(", ")));
	assertEquals("[1, 2, 3]", IntStream.of(1, 2, 3).boxed().map(String::valueOf).collect(Collectors.joining(", ", "[", "]")));
}

Wenn man also einen Stream von Objekten hat, kann man diesen mit der funktionalen Programmierweise weiterverarbeiten (filtern, transformieren) und ihn dann reduzieren, in diesem Fall in eine Zeichenkette mit einem festgelegten Trennzeichen und optionalen Präfixen und Suffixen.

Wer noch andere interessante Beispiele für split und join hat, darf diese gern in den Kommentaren hinterlassen.

Short URL for this post: http://wp.me/p4nxik-2s3
This entry was posted in Java and Quality, Java Basics and tagged , , , , . Bookmark the permalink.

Leave a Reply