Лучший способ преобразовать ArrayList в строку

У меня есть объект, ArrayList который я хочу полностью вывести в виде строки. По сути, я хочу вывести его по порядку, используя toString каждый элемент, разделенный вкладками. Есть ли быстрый способ сделать это? Вы можете пропустить его (или удалить каждый элемент) и объединить его со строкой, но я думаю, что это будет очень медленно.

Ответов (25)

Решение

Java 8 представляет String.join(separator, list) метод; см . ответ Виталия Федеренко .

До Java 8 ArrayList единственным вариантом было использование цикла для перебора :

НЕ используйте этот код, продолжайте читать до конца этого ответа, чтобы понять, почему это нежелательно и какой код следует использовать вместо этого:

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

String listString = "";

for (String s : list)
{
    listString += s + "\t";
}

System.out.println(listString);

На самом деле, конкатенация строк вполне javac подойдет , поскольку компилятор append в StringBuilder любом случае оптимизирует конкатенацию строк как серию операций над объектом. Вот часть разборки байт-кода из for цикла из приведенной выше программы:

   61:  new #13; //class java/lang/StringBuilder
   64:  dup
   65:  invokespecial   #14; //Method java/lang/StringBuilder."<init>":()V
   68:  aload_2
   69:  invokevirtual   #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   72:  aload   4
   74:  invokevirtual   #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   77:  ldc #16; //String \t
   79:  invokevirtual   #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   82:  invokevirtual   #17; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

Как видно, компилятор оптимизирует этот цикл с помощью a StringBuilder, поэтому производительность не должна быть большой проблемой.

(Хорошо, на второй взгляд, StringBuilder экземпляр создается на каждой итерации цикла, поэтому это может быть не самый эффективный байт-код. Создание экземпляра и использование явного StringBuilder, вероятно, даст лучшую производительность.)

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

Изменить: как указано в комментариях, вышеупомянутая оптимизация компилятора действительно создает новый экземпляр StringBuilder на каждой итерации. (Что я уже отмечал ранее.)

Наиболее оптимизированным методом будет ответ Пола Томблина , поскольку он создает только один StringBuilder объект вне for цикла.

Переписываем приведенный выше код на:

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

StringBuilder sb = new StringBuilder();
for (String s : list)
{
    sb.append(s);
    sb.append("\t");
}

System.out.println(sb.toString());

Будет только StringBuilder один раз создавать экземпляр вне цикла и делать только два вызова append метода внутри цикла, как показано в этом байт-коде (который показывает создание экземпляра StringBuilder и цикла):

   // Instantiation of the StringBuilder outside loop:
   33:  new #8; //class java/lang/StringBuilder
   36:  dup
   37:  invokespecial   #9; //Method java/lang/StringBuilder."<init>":()V
   40:  astore_2

   // [snip a few lines for initializing the loop]
   // Loading the StringBuilder inside the loop, then append:
   66:  aload_2
   67:  aload   4
   69:  invokevirtual   #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   72:  pop
   73:  aload_2
   74:  ldc #15; //String \t
   76:  invokevirtual   #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   79:  pop

Итак, действительно, ручная оптимизация должна быть более эффективной, поскольку внутренняя часть for цикла короче и нет необходимости создавать экземпляр a StringBuilder на каждой итерации.

Если вы делаете это на Android, для этого есть хорошая утилита TextUtils, у которой есть .join(String delimiter, Iterable) метод.

List<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
String joined = TextUtils.join(", ", list);

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

Если вы искали быстрый однострочник, начиная с Java 5 вы можете сделать это:

myList.toString().replaceAll("\\[|\\]", "").replaceAll(", ","\t")

Кроме того, если ваша цель - просто распечатать содержимое и вас меньше беспокоит "\ t", вы можете просто сделать это:

myList.toString()

который возвращает строку вроде

[str1, str2, str3]

Если у вас есть массив (не ArrayList), вы можете сделать то же самое, например:

 Arrays.toString(myList).replaceAll("\\[|\\]", "").replaceAll(", ","\t")

Это довольно старый вопрос, но я полагаю, что мог бы добавить более современный ответ - используйте Joinerкласс из Guava :

String joined = Joiner.on("\t").join(list);

Если вы используете Eclipse Collections , вы можете использовать этот makeString() метод.

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

Assert.assertEquals(
    "one\ttwo\tthree",
    ArrayListAdapter.adapt(list).makeString("\t"));

Если вы можете преобразовать ваш ArrayList в файл FastList, вы можете избавиться от адаптера.

Assert.assertEquals(
    "one\ttwo\tthree",
    FastList.newListWith("one", "two", "three").makeString("\t"));

Примечание: я являюсь приверженцем коллекций Eclipse.

В Java 8 или новее:

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

Если list тип не String, можно использовать соединяющий коллектор:

String listString = list.stream().map(Object::toString)
                        .collect(Collectors.joining(", "));

Приведенный ниже код может вам помочь,

List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
String str = list.toString();
System.out.println("Step-1 : " + str);
str = str.replaceAll("[\\[\\]]", "");
System.out.println("Step-2 : " + str);

Выход:

Step-1 : [1, 2, 3]
Step-2 : 1, 2, 3

Может быть, не лучшим способом, но изящным способом.

Arrays.deepToString (Arrays.asList («Тест», «Тест2»)

import java.util.Arrays;

    public class Test {
        public static void main(String[] args) {
            System.out.println(Arrays.deepToString(Arrays.asList("Test", "Test2").toArray()));
        }
    }

Выход

[Test, Test2]

Изменение списка на читаемую и значимую строку - действительно частый вопрос, с которым может столкнуться каждый.

Случай 1 . Если у вас есть StringUtils apache в пути к классам (как из rogerdpack и Ravi Wallau):

import org.apache.commons.lang3.StringUtils;
String str = StringUtils.join(myList);

Случай 2 . Если вы хотите использовать только способы из JDK (7):

import java.util.Arrays;
String str = Arrays.toString(myList.toArray()); 

Просто никогда не создавайте колеса самостоятельно, не используйте цикл для этой однострочной задачи.

Если вы работаете на Android и еще не используете Jack (например, потому что он все еще не поддерживает Instant Run), и если вам нужен больший контроль над форматированием результирующей строки (например, вы хотели бы использовать символ новой строки как разделитель элементов), и вы используете / хотите использовать библиотеку StreamSupport (для использования потоков в Java 7 или более ранних версиях компилятора), вы можете использовать что-то вроде этого (я поместил этот метод в свой класс ListUtils):

public static <T> String asString(List<T> list) {
    return StreamSupport.stream(list)
            .map(Object::toString)
            .collect(Collectors.joining("\n"));
}

И, конечно же, не забудьте реализовать toString () в классе объектов списка.

В Android есть класс TextUtil, который вы можете использовать http://developer.android.com/reference/android/text/TextUtils.html.

String implode = TextUtils.join("\t", list);

Для разделения с использованием вкладок вместо использования println вы можете использовать print

ArrayList<String> mylist = new ArrayList<String>();

mylist.add("C Programming");
mylist.add("Java");
mylist.add("C++");
mylist.add("Perl");
mylist.add("Python");

for (String each : mylist)
{       
    System.out.print(each);
    System.out.print("\t");
}

Одной строкой: от [12,0,1,78,12] до 12 0 1 78 12

String srt= list.toString().replaceAll("\\[|\\]|,","");

Было бы хорошо следующее:

List<String> streamValues = new ArrayList<>();
Arrays.deepToString(streamValues.toArray()));   
List<String> stringList = getMyListOfStrings();
StringJoiner sj = new StringJoiner(" ");
stringList.stream().forEach(e -> sj.add(e));
String spaceSeparated = sj.toString()

Вы переходите к new StringJoiner последовательности символов, которую хотите использовать в качестве разделителя. Если вы хотите сделать CSV: new StringJoiner(", ");

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

String csv = String.join("\t", yourArray);

в противном случае у commons-lang есть метод join ():

String csv = org.apache.commons.lang3.StringUtils.join(yourArray, "\t");

Если вы не хотите, чтобы последний \ t после последнего элемента, вы должны использовать индекс для проверки, но помните, что это только «работает» (т.е. O (n)), когда списки реализуют RandomAccess.

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

StringBuilder sb = new StringBuilder(list.size() * apprAvg); // every apprAvg > 1 is better than none
for (int i = 0; i < list.size(); i++) {
    sb.append(list.get(i));
    if (i < list.size() - 1) {
        sb.append("\t");
    }
}
System.out.println(sb.toString());

Прокрутите его и вызовите toString. Не существует волшебного способа, и если бы он был, как вы думаете, что он делал бы под обложкой, кроме как перебирать его? Примерно единственной микрооптимизацией было бы использование StringBuilder вместо String, и даже это не большая победа - конкатенация строк превращается в StringBuilder под прикрытием, но, по крайней мере, если вы напишете это таким образом, вы можете увидеть, что происходит.

StringBuilder out = new StringBuilder();
for (Object o : list)
{
  out.append(o.toString());
  out.append("\t");
}
return out.toString();

В O(n) любом случае это алгоритм (если вы не сделали какое-то многопоточное решение, в котором вы разбили список на несколько подсписок, но я не думаю, что это то, о чем вы просите).

Просто используйте StringBuilder как показано ниже:

StringBuilder sb = new StringBuilder();

for (Object obj : list) {
  sb.append(obj.toString());
  sb.append("\t");
}

String finalString = sb.toString();

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

Загрузите Apache Commons Lang и используйте метод

 StringUtils.join(list)

 StringUtils.join(list, ", ") // 2nd param is the separator.

Конечно, вы можете реализовать это самостоятельно, но их код полностью протестирован и, вероятно, является наилучшей из возможных реализаций.

Я большой поклонник библиотеки Apache Commons и считаю ее отличным дополнением к стандартной библиотеке Java.

Для большинства проектов Java часто доступен язык apache-commons. Методы StringUtils.join () очень хороши и имеют несколько разновидностей, чтобы удовлетворить почти все потребности.

public static java.lang.String join(java.util.Collection collection,
                                    char separator)


public static String join(Iterator iterator, String separator) {
    // handle null, zero and one elements before building a buffer 
    Object first = iterator.next();
    if (!iterator.hasNext()) {
        return ObjectUtils.toString(first);
    }
    // two or more elements 
    StringBuffer buf = 
        new StringBuffer(256); // Java default is 16, probably too small 
    if (first != null) {
        buf.append(first);
    }
    while (iterator.hasNext()) {
        if (separator != null) {
            buf.append(separator);
        }
        Object obj = iterator.next();
        if (obj != null) {
            buf.append(obj);
        }
    }
    return buf.toString();
}

Параметры:

collection - Коллекция значений для объединения, может быть нулевым

separator - символ разделителя для использования

Возвращает : объединенная строка, null, если ввод нулевого итератора

Начиная с: 2.3

Элегантный способ справиться с завершающими символами разделения - использовать разделитель классов.

StringBuilder buf = new StringBuilder();
Separator sep = new Separator("\t");
for (String each: list) buf.append(sep).append(each);
String s = buf.toString();

Метод toString класса Separator возвращает разделитель, за исключением первого вызова. Таким образом, мы печатаем список без завершающих (или в данном случае) ведущих разделителей.

На данный момент это довольно старый разговор, и теперь Apache Commons внутри используют StringBuilder: http://commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line. 3045

Как мы знаем, это повысит производительность, но если производительность критична, то используемый метод может быть несколько неэффективным. В то время как интерфейс является гибким и обеспечивает согласованное поведение для разных типов коллекций, он несколько неэффективен для списков, которые являются типом коллекции в исходном вопросе.

Я основываю это на том, что мы несем некоторые накладные расходы, которых можно было бы избежать, просто перебирая элементы в традиционном цикле for. Вместо этого за кулисами происходят некоторые дополнительные вещи, проверяющие наличие одновременных модификаций, вызовов методов и т. Д. Расширенный цикл for, с другой стороны, приведет к таким же накладным расходам, поскольку итератор используется в объекте Iterable (List).

Для этого вы можете использовать Regex. Это настолько лаконично, насколько это возможно

System.out.println(yourArrayList.toString().replaceAll("\\[|\\]|[,][ ]","\t"));

В Java 8 все просто. См. Пример для списка целых чисел:

String result = Arrays.asList(1,2,3).stream().map(Object::toString).reduce((t, u) -> t + "\t" + u).orElse("");

Или многострочная версия (что проще читать):

String result = Arrays.asList(1,2,3).stream()
    .map(Object::toString)
    .reduce((t, u) -> t + "\t" + u)
    .orElse("");

Обновление - более короткая версия

String result = Arrays.asList(1,2,3).stream()
                .map(Object::toString)
                .collect(Collectors.joining("\t"));