Уменьшите список строк, добавьте префикс только к последнему элементу, используя потоки Java 8

Как я могу уменьшить или собрать список строк, разделенных запятой и префиксом «и» только до последнего элемента, используя потоки Java 8?

eg.

List<String> ls = Arrays.asList("tom","terry","john","kevin","steve");

String result = ls.stream().map(String::toString)
                .collect(Collectors.joining(", "));

System.out.println(result);

Этот оператор печатает => tom, terry, john, kevin, steve. Но я хотел напечатать список как Том, Терри, Джон, Кевин и Стив.


person Srivatsan    schedule 09.08.2016    source источник
comment
Вам нужно сделать новую строку и использовать 4 пробела в качестве отступа, чтобы выделить код. Читать будет намного легче!   -  person sascha10000    schedule 10.08.2016


Ответы (3)


Два решения.

Неуклюже, на самом деле беру подсписок:

    String result = ls.stream()
            .limit(ls.size() - 1)
            .collect(Collectors.joining(", ")) + " and " + ls.get(ls.size() - 1);

Использование класса для перемещения на одно место:

    class SbS {
        StringBuilder sb = new StringBuilder();
        String s = "";

        @Override
        public String toString() {
            return sb + (s.empty() ? "" : " and " + s);
        }
    }
    result = ls.stream()
            .collect(SbS::new,
                (a, s) -> {
                    a.sb.append(a.sb.length() == 0 ? "" : ", ").append(a.s); a.s = s;
                },
                (a1, a2) -> {}).toString();

Внимание: определение класса должно быть помещено внутри метода, как и выше. В противном случае потребуется static. Лямбда для (a, s) может быть методом в SbS. Поддержка параллелизма предоставляется читателю в качестве упражнения (a1, a1).

person Joop Eggen    schedule 10.08.2016

Потоки не делают это проще. С таким же успехом вы можете использовать String::replaceAll:

String input = "tom, terry, john, kevin, steve";
String result = input.replaceAll(", (\\w+)$", " and $1");

System.out.println(result);

Выход:

tom, terry, john, kevin and steve
person Jorn Vernee    schedule 09.08.2016
comment
Я не думаю, что map() нужен - он вызывает toString... для каждой строки. Говоря об этом, я не думаю, что даже поток необходим, учитывая, что в этом случае это просто более подробный способ выполнения String.join(, , ls) - person VLAZ; 10.08.2016
comment
@Влд Ты прав. Но я подозреваю, что у OP есть случай, когда входными данными являются не строки, а какой-то другой объект, и это был самый близкий пример к реальной ситуации. В основном я просто игнорировал, как был создан ввод для моей строки. - person Jorn Vernee; 10.08.2016
comment
Я бы пошел еще дальше: ls.toString().replaceAll("^.|.$", "").replaceAll(", (\\w+)$", " and $1") и обойти весь поток крушения поезда - person Bohemian♦; 11.08.2016

Stream API не предоставляет стандартного способа добиться этого. Моя библиотека StreamEx, которая расширяет стандартный Stream API, имеет файл mapLast, который здесь полезен. Это позволяет отображать только последний элемент потока, оставляя все остальное как есть:

System.out.println(StreamEx.of(ls).mapLast("and "::concat).joining(", "));

Результат следующий:

tom, terry, john, kevin, and steve

Обратите внимание, что перед «и» добавляется запятая, что допустимо (см. запятая). Если вам это не нравится (или на самом деле вы используете другой язык, который запрещает такую ​​пунктуацию), то это можно сделать немного длиннее:

StreamEx.of(ls)
        .mapLastOrElse(", "::concat, " and "::concat)
        .joining().substring(", ".length());
person Tagir Valeev    schedule 11.08.2016
comment
Проблема с оксфордской запятой в том, что она не должна появляться, когда элементов ровно два… - person Holger; 22.08.2016
comment
@ Хольгер, это определенно проблема. Добавление в мою библиотеку специального сборщика, решающего все эти случаи, было бы слишком специфичным, поскольку в других языках могут быть другие правила пунктуации. Вероятно, проект типа ICU4J мог бы предложить такой сборщик, если бы он был ориентирован на Java 8... - person Tagir Valeev; 22.08.2016
comment
Ну, форматирование структур данных для определенного человеческого языка — это не задача, для которой должно быть готовое решение (и оно не должно быть основано на Stream). В случае с пользовательским интерфейсом уровень техники по-прежнему заключается в наличии пакета ресурсов с предварительно созданными строками шаблонов… - person Holger; 22.08.2016