Присоединение к списку <String>в Java с запятыми и "и" - PullRequest
47 голосов
/ 24 ноября 2011

С учетом списка

List<String> l = new ArrayList<String>();
l.add("one");
l.add("two");
l.add("three");

У меня есть метод

String join(List<String> messages) {
        if (messages.isEmpty()) return "";
        if (messages.size() == 1) return messages.get(0);
        String message = "";
        message = StringUtils.join(messages.subList(0, messages.size() -2), ", ");
        message = message + (messages.size() > 2 ? ", " : "") + StringUtils.join(messages.subList(messages.size() -2, messages.size()), ", and ");
        return message;
    }

, который для l выдает «один, два и три».У меня вопрос, есть ли стандартный (apache-commons) метод, который делает то же самое? Например,

WhatEverUtils.join(l, ", ", ", and ");

Для пояснения.Моя проблема не в том, чтобы заставить этот метод работать.Работает так, как я хочу, проверено и все хорошо.Моя проблема в том, что я не смог найти какой-то apache-commons-like модуль, который реализует такую ​​функциональность.Это удивляет меня, так как я не могу быть первым, кто нуждается в этом.

Но тогда, возможно, все остальные только что сделали

StringUtils.join(l, ", ").replaceAll(lastCommaRegex, ", and");

Ответы [ 7 ]

51 голосов
/ 27 декабря 2015

В Java 8 вы можете использовать String.join(), например:

Collection<String> elements = ....;
String result = String.join(", ", elements);
17 голосов
/ 24 ноября 2011

Как насчет присоединиться из: org.apache.commons.lang.StringUtils

Пример:

StringUtils.join(new String[] { "one", "two", "three" }, ", "); // one, two, three

Иметь "«или», и «вы можете просто заменить последнюю запятую.

14 голосов
/ 24 ноября 2011

Мне нравится использовать Google коллекции для этой цели. Аккуратный и очень полезный:

Joiner.on(",").join(myList)

Этот вид кода был написан снова и снова, и вы должны быть освобождены, реализуя вашу конкретную логику реализации.

Если вы используете Maven, при этом зависимость:

<dependency>
  <groupId>com.google.collections</groupId>
  <artifactId>google-collections</artifactId>
  <version>1.0</version>
</dependency>

У него также есть куча других замечательных интересных функций!

РЕДАКТИРОВАТЬ: Ради полноты, это будет производить список "один, два и три".

List<String> originalList = Arrays.asList("one", "two", "three");
Joiner.on(", ").join(originalList.subList(0, originalList.size() - 1))
  .concat(", and ").concat(originalList.get(originalList.size() - 1));
11 голосов
/ 08 октября 2015

В Java 8 вы можете использовать потоки с присоединениями.

Collection<String> strings;
...
String commaDelimited = strings.stream().collect(Collectors.joining(","));
// use strings.parallelStream() instead, if you think
//   there are gains to be had by doing fork/join
4 голосов
/ 06 декабря 2017

Для получения грамматического вывода на английском языке необходимо учитывать 3 случая при конкатенации списка строк:

  1. "A"

  2. "A и B "

  3. " A, B и C.

Это может быть выполнено с использованием стандартной Java или Guava, как показано ниже. Решенияв основном то же самое и просто до предпочтения, что вы хотите использовать.

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;

import org.junit.Test;

import java.util.List;

import static org.junit.Assert.assertEquals;

public class JoinListTest {

    @Test
    public void test_join() {
        // create cases (don't need to use ImmutableList builder from guava)
        final List<String> case1 = new ImmutableList.Builder<String>().add("A").build();
        final List<String> case2 = new ImmutableList.Builder<String>().add("A", "B").build();
        final List<String> case3 = new ImmutableList.Builder<String>().add("A", "B", "C").build();
        // test with standard java
        assertEquals("A", joinListGrammaticallyWithJava(case1));
        assertEquals("A and B", joinListGrammaticallyWithJava(case2));
        assertEquals("A, B, and C", joinListGrammaticallyWithJava(case3));
        // test with guava
        assertEquals("A", joinListGrammaticallyWithGuava(case1));
        assertEquals("A and B", joinListGrammaticallyWithGuava(case2));
        assertEquals("A, B, and C", joinListGrammaticallyWithGuava(case3));
    }

    private String joinListGrammaticallyWithJava(final List<String> list) {
        return list.size() > 1
                ? String.join(", ", list.subList(0, list.size() - 1))
                    .concat(String.format("%s and ", list.size() > 2 ? "," : ""))
                    .concat(list.get(list.size() - 1))
                : list.get(0);
    }

    private String joinListGrammaticallyWithGuava(final List<String> list) {
        return list.size() > 1
                ? Joiner.on(", ").join(list.subList(0, list.size() - 1))
                    .concat(String.format("%s and ", list.size() > 2 ? "," : ""))
                    .concat(list.get(list.size() - 1))
                : list.get(0);
    }

}
4 голосов
/ 11 сентября 2013

В других ответах говорится о «замене последней запятой», что небезопасно в том случае, если последний термин содержит запятую.

Вместо использования библиотеки вы можете просто использовать одну (хотя и длинную) строку кода JDK:

public static String join(List<String> msgs) {
    return msgs == null || msgs.size() == 0 ? "" : msgs.size() == 1 ? msgs.get(0) : msgs.subList(0, msgs.size() - 1).toString().replaceAll("^.|.$", "") + " and " + msgs.get(msgs.size() - 1);
}

См. живую демонстрацию этого кода, обрабатывающую все крайние случаи.


К вашему сведению, вот более читаемый двухслойный текст:

public static String join(List<String> msgs) {
    int size = msgs == null ? 0 : msgs.size();
    return size == 0 ? "" : size == 1 ? msgs.get(0) : msgs.subList(0, --size).toString().replaceAll("^.|.$", "") + " and " + msgs.get(size);
}
0 голосов
/ 24 ноября 2011

Я не знаю ни одного столяра Apache String, который бы поддерживал добавление and в присоединенную строку.

Вот непроверенный код, который будет делать то, что вы просили:

public static String join(String separator, List<String> mList, boolean includeAndInText) {
    StringBuilder sb = new StringBuilder();
    int count = 0;

    for (String m: mList) {
        if (includeAndInText && (count + 1 != mList.size())) {
            sb.append (" and ");
        }

        sb.append(m);
        count++;
        if (count < mList.size()) {
            sp.append(separator);
        }       
    }

    return sb.toString();
}
...