Самый простой способ разделить список запятыми?

Каков самый простой способ разделить список через запятую в Java?

Я знаю несколько способов сделать это, но мне интересно, какой из них лучший (где «лучший» означает самый ясный и / или самый короткий, а не самый эффективный.

У меня есть список, и я хочу перебрать его, печатая каждое значение. Я хочу вывести запятую между каждым элементом, но не после последнего (и не перед первым).

List --> Item ( , Item ) *
List --> ( Item , ) * Item

Пример раствора 1:

boolean isFirst = true;
for (Item i : list) {
  if (isFirst) {
    System.out.print(i);        // no comma
    isFirst = false;
  } else {
    System.out.print(", "+i);   // comma
  }
}

Пример решения 2 - создать подсписок:

if (list.size()>0) {
  System.out.print(list.get(0));   // no comma
  List theRest = list.subList(1, list.size());
  for (Item i : theRest) {
    System.out.print(", "+i);   // comma
  }
}

Пример раствора 3:

  Iterator<Item> i = list.iterator();
  if (i.hasNext()) {
    System.out.print(i.next());
    while (i.hasNext())
      System.out.print(", "+i.next());
  }

Они специально относятся к первому предмету; вместо этого можно было относиться к последнему специально.

Кстати, вот как реализовано List toString (унаследовано от AbstractCollection) в Java 1.6:

public String toString() {
    Iterator<E> i = iterator();
    if (! i.hasNext())
        return "[]";

    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {
        E e = i.next();
        sb.append(e == this ? "(this Collection)" : e);
        if (! i.hasNext())
            return sb.append(']').toString();
        sb.append(", ");
    }
}

Он выходит из цикла раньше, чтобы не ставить запятую после последнего элемента. Кстати: это первый раз, когда я вспоминаю, что видел "(этот сборник)"; вот код, чтобы спровоцировать это:

List l = new LinkedList();
l.add(l);
System.out.println(l);

Я приветствую любое решение, даже если оно использует неожиданные библиотеки (регулярное выражение?); а также решения на языках, отличных от Java (например, я думаю, что в Python / Ruby есть функция intersperse - как это реализовано?).

Уточнение: под библиотеками я имею в виду стандартные библиотеки Java. Что касается других библиотек, я рассматриваю их с другими языками и мне интересно узнать, как они реализованы.

В наборе инструментов EDIT упоминался аналогичный вопрос: Последняя итерация расширенного цикл for в Java

И еще: Последний элемент в цикле заслуживают отдельного рассмотрения?


person 13ren    schedule 21.03.2009    source источник
comment
Возможный дубликат Что лучше как создать строку элементов с разделителями на Java?   -  person Raedwald    schedule 23.02.2016


Ответы (28)


Java 8 и новее

Использование класса StringJoiner и _ 2 _ метод:

StringJoiner joiner = new StringJoiner(",");
list.forEach(item -> joiner.add(item.toString());
return joiner.toString();

Используя Stream и _ 5_:

return list.stream().
       map(Object::toString).
       collect(Collectors.joining(",")).toString();

Java 7 и более ранние версии

См. Также # 285523.

String delim = "";
for (Item i : list) {
    sb.append(delim).append(i);
    delim = ",";
}
person toolkit    schedule 21.03.2009
comment
Очень удивительный способ хранить состояние! Это почти объектно-ориентированный полиморфизм. Мне не нравится ненужное присваивание в каждом цикле, но я уверен, что это более эффективно, чем if. Большинство решений повторяют тест, который, как мы знаем, не будет правдой - хотя они и неэффективны, они, вероятно, самые четкие. Спасибо за ссылку! - person 13ren; 21.03.2009
comment
Мне это нравится, но из-за того, что это умно и удивительно, я подумал, что использовать его было бы неуместно (удивлять - нехорошо!). Однако я ничего не мог с собой поделать. Я все еще не уверен в этом; однако он настолько короткий, что его легко понять, если есть комментарий, который вам подсказывает. - person 13ren; 25.03.2009
comment
@ 13ren: Вам с дядей Бобом нужно поговорить о коде, который является умным и удивительным. - person Stefan Kendall; 31.08.2011
comment
Мне кажется, что я зря потратил годы, не зная об этом ^^; - person Lake; 10.08.2015
comment
Зачем Java превращать такую ​​задачу в уродливый код? - person Eduardo; 25.10.2015
comment
@eduardo - я знаю. Clojure имеет гораздо более простой синтаксис: (clojure.string/join "," my list) - person toolkit; 09.12.2017
comment
Спасибо, что познакомили меня со StringJoiner .. люблю это - person Atul Kumbhar; 09.08.2019

В Java 8 есть несколько новых способов сделать это:

Пример:

// Util method for strings and other char sequences
List<String> strs = Arrays.asList("1", "2", "3");
String listStr1 = String.join(",", strs);

// For any type using streams and collectors
List<Object> objs = Arrays.asList(1, 2, 3);
String listStr2 = objs.stream()
    .map(Object::toString)
    .collect(joining(",", "[", "]"));

// Using the new StringJoiner class
StringJoiner joiner = new StringJoiner(", ", "[", "]");
joiner.setEmptyValue("<empty>");
for (Integer i : objs) {
  joiner.add(i.toString());
}
String listStr3 = joiner.toString();

Подход с использованием потоков предполагает import static java.util.stream.Collectors.joining;.

person Lii    schedule 02.09.2014
comment
Это ИМХО, если вы используете java 8, лучший ответ. - person scravy; 03.11.2015

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

Столяр.

person Stefan Kendall    schedule 31.08.2011
comment
Я пришел сюда в поисках чего-то, что делает именно это. Спасибо! +1 - person javamonkey79; 26.03.2013
comment
Такой же. Спасибо за это. Наконец-то кто-то вменяемый. - person T.W.R. Cole; 18.06.2014

Если вы используете Spring Framework, вы можете сделать это с помощью StringUtils:

public static String arrayToDelimitedString(Object[] arr)
public static String arrayToDelimitedString(Object[] arr, String delim)
public static String collectionToCommaDelimitedString(Collection coll)
public static String collectionToCommaDelimitedString(Collection coll, String delim)
person Henryk Konsek    schedule 21.03.2009
comment
Спасибо, а как это реализовано? - person 13ren; 22.03.2009

На основе реализации Java List toString:

Iterator i = list.iterator();
for (;;) {
  sb.append(i.next());
  if (! i.hasNext()) break;
  ab.append(", ");
}

Он использует такую ​​грамматику:

List --> (Item , )* Item

Будучи основанным на последнем, а не на первом, он может проверять пропущенную запятую с помощью того же теста, чтобы проверить наличие конца списка. Я думаю, что это очень элегантно, но я не уверен в ясности.

person 13ren    schedule 21.03.2009

Есть отличный способ добиться этого с помощью Java 8:

List<String> list = Arrays.asList(array);
String joinedString = String.join(",", list);
person Krishnanunni P V    schedule 09.05.2016
comment
TextUtils.join (,, список); - person Arul Pandian; 17.06.2019

Один из вариантов цикла foreach:

StringBuilder sb = new StringBuilder();
for(String s:list){
  if (sb.length()>0) sb.append(",");
  sb.append(s);
}
person BAH    schedule 26.10.2010

StringBuffer result = new StringBuffer();
for(Iterator it=list.iterator; it.hasNext(); ) {
  if (result.length()>0)
    result.append(", ");
  result.append(it.next());
}

Обновление: как Дэйв Уэбб упомянул в комментариях, это может не дать правильных результатов, если первые элементы в списке являются пустыми строками.

person cherouvim    schedule 21.03.2009
comment
Мех .. все еще в цикле есть if. - person sfossen; 21.03.2009
comment
Вы можете использовать цикл foreach, чтобы сделать его короче. - person Peter Lawrey; 21.03.2009
comment
Это не сработает, если первый элемент в списке - пустая строка. - person Dave Webb; 21.03.2009
comment
@ Дэйв Уэбб: Да, спасибо. Но в вопросе такого требования нет. - person cherouvim; 21.03.2009
comment
@cherovium: в вопросе не говорится, что ни один из элементов не будет пустой строкой, поэтому это неявное требование. Если вы создаете файл CSV, пустые поля могут быть довольно обычным явлением. - person Dave Webb; 21.03.2009
comment
Оказывается, Джон Скит тоже дал этот ответ (то есть вы в хорошей компании): stackoverflow.com/questions/285523/ - person 13ren; 21.03.2009
comment
Ради всего святого, используйте StringBuilder - person scravy; 03.11.2015

Я обычно так делаю:

StringBuffer sb = new StringBuffer();
Iterator it = myList.iterator();
if (it.hasNext()) { sb.append(it.next().toString()); }
while (it.hasNext()) { sb.append(",").append(it.next().toString()); }

Хотя я думаю, что с этого момента я буду проходить эту проверку в соответствии с реализацией Java;)

person Alex Marshall    schedule 21.03.2009
comment
Та же логика, что и в примере 3, мне он нравится, потому что в нем нет ничего лишнего. Однако я не знаю, насколько это ясно. Кстати: это немного эффективнее, если вы поместите while внутри if, избегая двух тестов, когда список пуст. Это также проясняет мне ситуацию. - person 13ren; 21.03.2009
comment
Ради всего святого, используйте StringBuilder - person scravy; 03.11.2015

Если вы можете использовать Groovy (который работает на JVM):

def list = ['a', 'b', 'c', 'd']
println list.join(',')
person kungfoo    schedule 21.03.2009
comment
спасибо, но меня интересуют реализации. Как это реализовано? - person 13ren; 22.03.2009

(Скопируйте и вставьте мой собственный ответ здесь. Многие из описанных здесь решений немного переборщили, ИМХО, особенно те, которые полагаются на внешние библиотеки. Есть хорошая чистая, ясная идиома для создания списка, разделенного запятыми, который я всегда использовал. Он полагается на условный оператор (?):

Изменить: исходное решение правильное, но не оптимальное согласно комментариям. Пробуем второй раз:

int[] array = {1, 2, 3};
StringBuilder builder = new StringBuilder();
for (int i = 0 ;  i < array.length; i++)
       builder.append(i == 0 ? "" : ",").append(array[i]);

Вот и все, 4 строчки кода, включая объявление массива и StringBuilder.

2-е изменение: если вы имеете дело с итератором:

    List<Integer> list = Arrays.asList(1, 2, 3);
    StringBuilder builder = new StringBuilder();
    for (Iterator it = list.iterator(); it.hasNext();)
        builder.append(it.next()).append(it.hasNext() ? "," : "");
person Julien Chastang    schedule 21.03.2009
comment
Это и stackoverflow.com/questions/668952/ выше аналогичны. Это коротко и понятно, но если у вас есть только итератор, вам сначала нужно преобразовать. Неэффективно, но, может быть, оно того стоит для ясности такого подхода? - person 13ren; 22.03.2009
comment
Ух, конкатенация строк создает временный StringBuilder при использовании StringBuilder. Неэффективно. Лучше (больше похоже на Java, но лучше работает код) было бы, если (i ›0) {builder.append (','); }, за которым следует builder.append (array [i]); - person Eddie; 22.03.2009
comment
Да, если вы посмотрите в байт-код, вы правы. Не могу поверить, что такой простой вопрос может стать таким сложным. Интересно, может ли компилятор здесь оптимизироваться. - person Julien Chastang; 22.03.2009
comment
Обеспечено второе, надеюсь, лучшее решение. - person Julien Chastang; 22.03.2009
comment
Может быть, лучше их разделить (чтобы люди могли проголосовать за одно или другое). - person 13ren; 24.03.2009
comment
Я предполагал, что этот стиль тестирования работает только с массивами, а не с итераторами - хороший вариант! Кстати: у вас есть два теста hasNext () - их можно объединить следующим образом: stackoverflow.com/questions/668952/ Короче - приятно, но что яснее? - person 13ren; 24.03.2009
comment
То есть, один hasNext () может как выйти из цикла, так и пропустить последнюю запятую. - person 13ren; 24.03.2009

Я обычно использую что-то похожее на версию 3. Работает хорошо c / c ++ / bash / ...: P

person sfossen    schedule 21.03.2009

Я не компилировал его ... но должен работать (или быть близок к работе).

public static <T> String toString(final List<T> list, 
                                  final String delim)
{
    final StringBuilder builder;

    builder = new StringBuilder();

    for(final T item : list)
    {
        builder.append(item);
        builder.append(delim);
    }

    // kill the last delim
    builder.setLength(builder.length() - delim.length());

    return (builder.toString());
}
person TofuBeer    schedule 22.03.2009
comment
Мне нравится этот способ, я бы предпочел другое форматирование. Но упомяну одно небольшое исправление: если список пуст, это вызовет исключение IndexOutOfBounds. Итак, либо сделайте проверку перед setLength, либо используйте Math.max (0, ...) - person tb-; 29.10.2012

Это очень коротко, очень ясно, но придает моей чувствительности ползучий ужас. Также немного неудобно адаптироваться к различным разделителям, особенно если это String (не char).

for (Item i : list)
  sb.append(',').append(i);
if (sb.charAt(0)==',') sb.deleteCharAt(0);

На основе: Последняя итерация расширенного цикла for в java

person 13ren    schedule 21.03.2009

Мне немного нравится этот подход, который я нашел некоторое время назад в блоге. К сожалению, я не помню имя / URL блога.

Вы можете создать служебный / вспомогательный класс, который будет выглядеть так:

private class Delimiter
{
    private final String delimiter;
    private boolean first = true;

    public Delimiter(String delimiter)
    {
        this.delimiter = delimiter;
    }

    @Override
    public String toString()
    {
        if (first) {
            first = false;
            return "";
        }

        return delimiter;
    }
}

Использовать вспомогательный класс просто:

StringBuilder sb = new StringBuilder();
Delimiter delimiter = new Delimiter(", ");

for (String item : list) {
    sb.append(delimiter);
    sb.append(item);
}
person Tobias Müller    schedule 23.03.2009
comment
Замечание: он должен быть разделителем, а не разделителем. - person Michael Myers; 23.03.2009

Поскольку вашим разделителем является "," вы можете использовать любое из следующего:

public class StringDelim {

    public static void removeBrackets(String string) {
        System.out.println(string.substring(1, string.length() - 1));
    }

    public static void main(String... args) {
        // Array.toString() example
        String [] arr = {"Hi" , "My", "name", "is", "br3nt"};
        String string = Arrays.toString(arr);
        removeBrackets(string);

        // List#toString() example
        List<String> list = new ArrayList<String>();
        list.add("Hi");
        list.add("My");
        list.add("name");
        list.add("is");
        list.add("br3nt");
        string = list.toString();
        removeBrackets(string);

        // Map#values().toString() example
        Map<String, String> map = new LinkedHashMap<String, String>();
        map.put("1", "Hi");
        map.put("2", "My");
        map.put("3", "name");
        map.put("4", "is");
        map.put("5", "br3nt");
        System.out.println(map.values().toString());
        removeBrackets(string);

        // Enum#toString() example
        EnumSet<Days> set = EnumSet.allOf(Days.class);
        string = set.toString();
        removeBrackets(string);
    }

    public enum Days {
        MON("Monday"),
        TUE("Tuesday"),
        WED("Wednesday"),
        THU("Thursday"),
        FRI("Friday"),
        SAT("Saturday"),
        SUN("Sunday");

        private final String day;

        Days(String day) {this.day = day;}
        public String toString() {return this.day;}
    }
}

Если ваш разделитель НИЧЕГО другого, то это не сработает для вас.

person br3nt    schedule 10.02.2012

Мне нравится это решение:

String delim = " - ", string = "";

for (String item : myCollection)
    string += delim + item;

string = string.substring(delim.length());

Я предполагаю, что он также может использовать StringBuilder.

person br3nt    schedule 10.02.2012

На мой взгляд, это проще всего прочитать и понять:

StringBuilder sb = new StringBuilder();
for(String string : strings) {
    sb.append(string).append(',');
}
sb.setLength(sb.length() - 1);
String result = sb.toString();
person SergeyB    schedule 22.11.2013

В Python это просто

",". присоединиться (ваш список)

В C # есть статический метод класса String

String.Join (",", ваш список строк)

Извините, не уверен насчет Java, но подумал, что отвечу, когда вы спросите о других языках. Я уверен, что в Java было бы что-то подобное.

person tarn    schedule 21.03.2009
comment
IIRC, вы получаете трейлинг в версии .Net. :-( - person ; 21.03.2009
comment
Только что протестировано, без конечного разделителя в .NET или Python - person tarn; 21.03.2009
comment
Благодарность! Я нашел его источник: svn .python.org / view / python / branch / release22-branch / Objects / найдите Catenate everything. Он опускает разделитель для последнего элемента. - person 13ren; 21.03.2009
comment
@ 13ren Отлично сделано! Помогает? У меня была блокировка, и я быстро вспомнил, почему в наши дни решил не программировать на C: P - person tarn; 21.03.2009
comment
@tarn, да, это интересный пример, потому что он добавляет команду, только если это не последний элемент: if (i ‹seqlen - 1) {‹ добавить разделитель ›} - person 13ren; 21.03.2009

Вы также можете безоговорочно добавить строку разделителя, а после цикла удалить лишний разделитель в конце. Тогда "если список пуст, то вернуть эту строку" в начале позволит вам избежать проверки в конце (так как вы не можете удалить символы из пустого списка)

Итак, действительно вопрос:

«Как вы думаете, какой способ собрать их вместе, имея петлю и if, является наиболее ясным?»

person Thorbjørn Ravn Andersen    schedule 21.03.2009

if (array.length>0)          // edited in response Joachim's comment
  sb.append(array[i]);
for (int i=1; i<array.length; i++)
  sb.append(",").append(array[i]);

На основе Самый простой способ разделить список запятыми (Java )?

Используя эту идею: Последний элемент в петля заслуживает отдельного рассмотрения?

person 13ren    schedule 22.03.2009
comment
Чтобы это сработало, вам нужно знать, что в списке есть хотя бы 1 элемент, иначе ваш первый цикл for выйдет из строя с исключением IndexOutOfBoundsException. - person Joachim H. Skeie; 09.10.2011
comment
@Joachim спасибо, ты конечно прав. Какое уродливое решение я написал! Я заменю for (int i=0; i<1; i++) на if (array.length>0). - person 13ren; 10.10.2011

Ни один из ответов пока не использует рекурсию ...

public class Main {

    public static String toString(List<String> list, char d) {
        int n = list.size();
        if(n==0) return "";
        return n > 1 ? Main.toString(list.subList(0, n - 1), d) + d
                  + list.get(n - 1) : list.get(0);
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList(new String[]{"1","2","3"});
        System.out.println(Main.toString(list, ','));
    }

}
person langsweirdt    schedule 12.03.2013

Метод

String join(List<Object> collection, String delimiter){
    StringBuilder stringBuilder = new StringBuilder();
    int size = collection.size();
    for (Object value : collection) {
        size --;
        if(size > 0){
            stringBuilder.append(value).append(delimiter);
        }
    }

    return stringBuilder.toString();
}

использование

Учитывая массив [1,2,3]

join(myArray, ",") // 1,2,3
person Ilya Gazman    schedule 29.09.2016

person    schedule
comment
Нашел источник: svn.apache.org/viewvc/commons/proper/lang/trunk/src/java/org/ Метод соединения имеет 9 перегрузок. Итерационные решения похожи на Пример решения 3; основанные на индексе используют: if (i ›startIndex) {‹ добавить разделитель ›} - person 13ren; 21.03.2009
comment
Хотя я действительно после реализаций. Хорошая идея - заключить это в функцию, но на практике мой разделитель иногда является новой строкой, и каждая строка также имеет отступ до определенной глубины. Если ... join (list, \ n + indent) ... это всегда сработает? Извините, просто подумал вслух. - person 13ren; 21.03.2009
comment
На мой взгляд, это самое красивое и кратчайшее решение. - person Jesper Rønn-Jensen; 17.03.2011
comment
наверное, list.iterator ()? - person Vanuan; 30.08.2013

person    schedule
comment
Да, это немного загадочно, но это альтернатива тому, что уже было опубликовано. - person cherouvim; 21.03.2009
comment
Я думаю, что код выглядит нормально, ИМХО он красивее, чем ваш другой ответ, и его вряд ли "загадочно". Оберните это методом под названием Join и смехом, но все же я ожидал бы, что у языка будет лучший способ решения действительно распространенной проблемы. - person tarn; 21.03.2009
comment
@tarn: вы думаете, что это хорошо, потому что у вас есть фон Python / perl;) Мне это тоже нравится - person cherouvim; 21.03.2009

person    schedule
comment
Я считаю, что это очень легко встроить в статический служебный класс, но при этом его очень легко читать и понимать. Также не требуются какие-либо дополнительные переменные или состояние, кроме самого списка, цикла и разделителя. - person Joachim H. Skeie; 09.10.2011
comment
Это даст что-то вроде: Item1Item2, Item3, Item4, - person ; 13.03.2013
comment
Ради всего святого, используйте StringBuilder - person scravy; 03.11.2015

person    schedule
comment
Я думаю, вы хотели сказать listToString.substring(0, listToString.length()-separator.length()); - person 13ren; 23.09.2010

person    schedule
comment
Только что заметил, что это похоже на ответ, который дал TofuBeer - person Eblis; 22.11.2011