Лучший способ конкатенации списка объектов String?

Каков наилучший способ конкатенации списка объектов String? Я думаю об этом:

List<String> sList = new ArrayList<String>();

// add elements

if (sList != null)
{
    String listString = sList.toString();
    listString = listString.subString(1, listString.length() - 1);
}

Я почему-то нашел это более аккуратным, чем использование подхода StringBuilder/StringBuffer.

Любые мысли/комментарии?

Ответ 1

Ваш подход зависит от реализации Java ArrayList # toString().

В то время как реализация документирована в Java API и вряд ли изменится, есть шанс. Это намного надежнее реализовать это самостоятельно (петли, StringBuilders, рекурсия, что вам больше нравится).

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

Ответ 2

Используйте один из методов StringUtils.join в Apache Commons Lang.

import org.apache.commons.lang3.StringUtils;

String result = StringUtils.join(list, ", ");

Если вам посчастливилось использовать Java 8, тогда это еще проще... просто используйте String.join

String result = String.join(", ", list);

Ответ 3

Использование Java 8 +

String str = list.stream().collect(Collectors.joining())

или даже

String str = String.join("", list);

Ответ 4

Вариант ответа codefin

public static String concatStringsWSep(Iterable<String> strings, String separator) {
    StringBuilder sb = new StringBuilder();
    String sep = "";
    for(String s: strings) {
        sb.append(sep).append(s);
        sep = separator;
    }
    return sb.toString();                           
}

Ответ 5

Если вы разрабатываете для Android, TextUtils.join предоставляется SDK.

Ответ 6

Guava - довольно аккуратная библиотека от Google:

Joiner joiner = Joiner.on(", ");
joiner.join(sList);

Ответ 7

Это самый элегантный и чистый способ, который я нашел до сих пор:

list.stream().collect(Collectors.joining(delimiter));

Ответ 9

Я предпочитаю String.join(список) в Java 8

Ответ 10

Вместо того, чтобы в зависимости от реализации ArrayList.toString() вы можете написать однострочный, если вы используете java 8:

String result = sList.stream()
                     .reduce("", String::concat);

Если вы предпочитаете использовать StringBuffer вместо String, так как String::concat имеет время выполнения O(n^2), вы можете сначала преобразовать все String в StringBuffer.

StringBuffer result = sList.stream()
                           .map(StringBuffer::new)
                           .reduce(new StringBuffer(""), StringBuffer::append);

Ответ 11

Я почему-то нашел это более аккуратным, чем с помощью StringBuilder/StringBuffer Подход.

Я думаю, это зависит от того, какой подход вы приняли.

Метод AbstractCollection # toString() просто выполняет итерацию по всем элементам и добавляет их в StringBuilder. Таким образом, ваш метод может сохранять несколько строк кода, но за счет дополнительных манипуляций со строками. Является ли этот компромисс хорошим, зависит от вас.

Ответ 12

Мне кажется, что StringBuilder будет быстрым и эффективным.

Основная форма будет выглядеть примерно так:

public static String concatStrings(List<String> strings)
{
    StringBuilder sb = new StringBuilder();
    for(String s: strings)
    {
        sb.append(s);
    }
    return sb.toString();       
}

Если это слишком упрощенное (и, вероятно, это так), вы можете использовать подобный подход и добавить разделитель следующим образом:

    public static String concatStringsWSep(List<String> strings, String separator)
{
    StringBuilder sb = new StringBuilder();
    for(int i = 0; i < strings.size(); i++)
    {
        sb.append(strings.get(i));
        if(i < strings.size() - 1)
            sb.append(separator);
    }
    return sb.toString();               
}

Я согласен с другими, которые ответили на этот вопрос, когда говорят, что вы не должны полагаться на метод toString() Java ArrayList.

Ответ 13

Предполагая, что быстрее просто переместить указатель/установить байт в нуль (или, тем не менее, Java реализует StringBuilder # setLength), а не проверять условие каждый раз через цикл, чтобы увидеть, когда добавить разделитель, вы можете использовать этот метод

public static String Intersperse (Collection<?> collection, String delimiter)
{
    StringBuilder sb = new StringBuilder ();
    for (Object item : collection)
    {
        if (item == null) continue;
        sb.append (item).append (delimiter);
    }
    sb.setLength (sb.length () - delimiter.length ());
    return sb.toString ();
}

Ответ 14

Следующая вариация на Питера Лоури отвечает без инициализации новой строки при каждом повороте цикла

String concatList(List<String> sList, String separator)
{
    Iterator<String> iter = sList.iterator();
    StringBuilder sb = new StringBuilder();

    while (iter.hasNext())
    {
        sb.append(iter.next()).append( iter.hasNext() ? separator : "");
    }
    return sb.toString();
}

Ответ 15

ArrayList наследует свой toString() -метод из AbstractCollection, то есть:

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(", ");
    }
}

Построение самой строки будет намного более эффективным.


Если вы действительно хотите заранее скопировать строки в какой-то список, вы должны предоставить свой собственный метод для эффективного их объединения, например. например:

static String join(Collection<?> items, String sep) {
    if(items.size() == 0)
        return "";

    String[] strings = new String[items.size()];
    int length = sep.length() * (items.size() - 1);

    int idx = 0;
    for(Object item : items) {
        String str = item.toString();
        strings[idx++] = str;
        length += str.length();
    }

    char[] chars = new char[length];
    int pos = 0;

    for(String str : strings) {
        str.getChars(0, str.length(), chars, pos);
        pos += str.length();

        if(pos < length) {
            sep.getChars(0, sep.length(), chars, pos);
            pos += sep.length();
        }
    }

    return new String(chars);
}

Ответ 16

В java 8 вы также можете использовать редуктор, что-то вроде:

public static String join(List<String> strings, String joinStr) {
    return strings.stream().reduce("", (prev, cur) -> prev += (cur + joinStr));
}

Ответ 17

В зависимости от потребности в производительности и количестве добавляемых элементов это может быть приемлемым решением. Если количество элементов велико, перераспределение памяти Arraylist может быть немного медленнее, чем StringBuilder.

Ответ 18

Используя Функциональная библиотека Java, импортируйте их:

import static fj.pre.Monoid.stringMonoid;
import static fj.data.List.list;
import fj.data.List;

... тогда вы можете сделать это:

List<String> ss = list("foo", "bar", "baz");
String s = stringMonoid.join(ss, ", ");

Или общий способ, если у вас нет списка строк:

public static <A> String showList(List<A> l, Show<A> s) {
  return stringMonoid.join(l.map(s.showS_()), ", ");
}

Ответ 19

если у вас есть json в ваших зависимостях. Вы можете использовать new JSONArray(list).toString()