Преобразуйте цикл 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.