Ich habe die folgende for-Schleife, die eine Liste von Strings durchläuft und das erste Zeichen jedes Wortes in einer StringBuilder
speichert. Ich würde gerne wissen, wie ich dies in einen Lambda-Ausdruck umwandeln kann
StringBuilder chars = new StringBuilder();
for (String l : list) {
chars.append(l.charAt(0));
}
Angenommen, Sie rufen nachher toString()
für die StringBuilder
auf. Ich denke, Sie suchen nur nach Collectors.joining()
, nachdem Sie jede Zeichenfolge einem einteiligen Teilstring zugeordnet haben:
String result = list
.stream()
.map(s -> s.substring(0, 1))
.collect(Collectors.joining());
Beispielcode:
import Java.util.*;
import Java.util.stream.*;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
String result = list
.stream()
.map(s -> s.substring(0, 1))
.collect(Collectors.joining());
System.out.println(result); // fbb
}
}
Beachten Sie die Verwendung von substring
anstelle von charAt
, so dass wir immer noch einen Stream von Strings haben, mit dem wir arbeiten können.
Unzählige Möglichkeiten, dies zu tun - die einfachste Option : Bleiben Sie beim Hinzufügen zu einer StringBuilder
und tun Sie dies:
final StringBuilder chars = new StringBuilder();
list.forEach(l -> chars.append(l.charAt(0)));
Ohne viele Zwischenobjekte erstellen zu müssen, können Sie dies folgendermaßen tun:
StringBuilder sb = list.stream()
.mapToInt(l -> l.codePointAt(0))
.collect(StringBuilder::new,
StringBuilder::appendCodePoint,
StringBuilder::append);
Beachten Sie, dass die Verwendung von codePointAt
viel besser ist als charAt
, als ob Ihre Zeichenfolge mit einem Ersatzpaar beginnt. Wenn Sie charAt
verwenden, haben Sie möglicherweise ein unvorhersehbares Ergebnis.
Hier sind drei verschiedene Lösungen für dieses Problem. Jede Lösung filtert zuerst leere Zeichenfolgen, da sonst StringIndexOutOfBoundsException
ausgelöst werden kann.
Diese Lösung entspricht der Lösung von Tagir mit dem hinzugefügten Code zum Filtern von leeren Zeichenfolgen. Ich habe es hier hauptsächlich aufgenommen, um es mit den anderen zwei Lösungen zu vergleichen, die ich bereitgestellt habe.
List<String> list =
Arrays.asList("the", "", "quick", "", "brown", "", "fox");
StringBuilder builder = list.stream()
.filter(s -> !s.isEmpty())
.mapToInt(s -> s.codePointAt(0))
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append);
String result = builder.toString();
Assert.assertEquals("tqbf", result);
Die zweite Lösung verwendet Eclipse Collections und verwendet einen relativ neuen Containertyp namens CodePointAdapter
, der in Version 7.0 hinzugefügt wurde.
MutableList<String> list =
Lists.mutable.with("the", "", "quick", "", "brown", "", "fox");
LazyIntIterable iterable = list.asLazy()
.reject(String::isEmpty)
.collectInt(s -> s.codePointAt(0));
String result = CodePointAdapter.from(iterable).toString();
Assert.assertEquals("tqbf", result);
Die dritte Lösung verwendet erneut Eclipse Collections, jedoch mit injectInto
und StringBuilder
anstelle von CodePointAdapter
.
MutableList<String> list =
Lists.mutable.with("the", "", "quick", "", "brown", "", "fox");
StringBuilder builder = list.asLazy()
.reject(String::isEmpty)
.collectInt(s -> s.codePointAt(0))
.injectInto(new StringBuilder(), StringBuilder::appendCodePoint);
String result = builder.toString();
Assert.assertEquals("tqbf", result);
Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.
Einfacher Weg mit Methodenreferenz:
List<String> list = Arrays.asList("ABC", "CDE");
StringBuilder sb = new StringBuilder();
list.forEach(sb::append);
String concatString = sb.toString();