Лучший способ объединить список строковых объектов? [дубликат]



этот вопрос уже есть ответ здесь:



каков наилучший способ объединения списка строковых объектов? Я думаю сделать так:



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

// add elements

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


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



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

514   19  

19 ответов:

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

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

используйте один из the StringUtils.присоединяйтесь методы в Apache Commons Lang.

import org.apache.commons.lang3.StringUtils;

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

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

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

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

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

или даже

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

вариация ответа 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();                           
}

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

гуавы Это довольно аккуратная библиотека от Google:

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

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

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

вы видели эту запись в блоге ужасов кодирования?

печальная трагедия театра микро-оптимизации

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

Я предпочитаю строку.Регистрация (список) в Java 8

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

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

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

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

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

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

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

мне кажется, что 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() ArrayList Java.

предполагая, что быстрее просто переместить указатель / установить байт в null (или, Однако, 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 ();
}

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

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

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);
}

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

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

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

С помощью Функциональная 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_()), ", ");
}

если у вас есть JSON в зависимости.вы можете использовать new JSONArray(list).toString()

Comments

    Ничего не найдено.