Преобразуйте цикл for в конкатентную строку в выражение лямбда
У меня есть следующий цикл for, который выполняет итерацию по списку строк и сохраняет первый символ каждого слова в StringBuilder
. Я хотел бы знать, как я могу преобразовать это в выражение лямбда
StringBuilder chars = new StringBuilder();
for (String l : list) {
chars.append(l.charAt(0));
}
Ответы
Ответ 1
Предполагая, что вы вызываете toString()
на StringBuilder
после этого, я думаю, что вы просто ищете Collectors.joining()
после сопоставления каждой строки с одно- символьная подстрока:
String result = list
.stream()
.map(s -> s.substring(0, 1))
.collect(Collectors.joining());
Пример кода:
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
}
}
Обратите внимание на использование substring
вместо charAt
, поэтому у нас все еще есть поток строк для работы.
Ответ 2
Тонны способов сделать это - самый простой вариант: добавьте к StringBuilder
и выполните следующее:
final StringBuilder chars = new StringBuilder();
list.forEach(l -> chars.append(l.charAt(0)));
Ответ 3
Без создания многих промежуточных объектов String вы можете сделать это следующим образом:
StringBuilder sb = list.stream()
.mapToInt(l -> l.codePointAt(0))
.collect(StringBuilder::new,
StringBuilder::appendCodePoint,
StringBuilder::append);
Обратите внимание, что использование codePointAt
намного лучше, чем charAt
, как если бы ваша строка начиналась с суррогатной пары, используя charAt
, вы можете иметь непредсказуемый результат.
Ответ 4
Вот три различных решения этой проблемы. Каждое решение сначала фильтрует пустые строки, иначе StringIndexOutOfBoundsException
может быть выбрано.
Это решение такое же, как решение от Tagir с добавленным кодом для фильтрации пустых строк. Я включил его здесь в первую очередь для сравнения с двумя другими решениями, которые я предоставил.
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);
Второе решение использует Eclipse Collections и использует относительно новый тип контейнера под названием CodePointAdapter
, который был добавлен в версии 7.0.
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);
Третье решение снова использует Eclipse Collections, но injectInto
и StringBuilder
вместо 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);
Примечание. Я являюсь коммиттером для коллекций Eclipse.