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


353

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


4
или удалите каждый элемент. Будьте осторожны, удаление элементов из списка ArrayList - это большая НЕТ-НЕТ, поскольку ваш «цикл» займет квадратичное время из-за смещения элементов (при условии, что вы выполняете цикл от первого до последнего элемента).
Димитрий К,

Ответы:


329

По сути, использование цикла для итерации 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;

Как видно, компилятор оптимизирует этот цикл с помощью 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на каждой итерации.


8
Из соображений производительности рассмотрите возможность использования класса StringBuilder вместо простого добавления строк.
Джереми

5
Компилятор оптимизирует конкатенацию строк, но вы будете создавать новый объект StringBuilder при каждом выполнении цикла.
Педро Энрикес

4
-1 для использования + =, в данном случае это убийца производительности. Всегда используйте StringBuilder для повторного добавления в строку.
StarBlue

10
Это решение добавляет дополнительный «\ t» в конце строки, что часто нежелательно, особенно если вы хотите создать список через запятую или аналогичный. Я думаю, что другие решения, опубликованные здесь, с использованием Apache Commons или Guava, превосходят их.
Питер Гетц

3
Это не лучший ответ. Посмотрите ниже на Виталия для Javaили Geewax дляAndroid
Gibolt

896

В Java 8 или более поздней версии:

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

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

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

144
Я не могу поверить, что потребовалось до Java 8, чтобы добавить это.
Пол

45
Этот ответ должен быть проголосовал больше! Нет хаков, нет библиотек и петель.
Сонго

4
это не работает для того, что сказал ОП. String.join принимает в качестве второго аргумента Iterable <? расширяет CharSequence>. Так что это работает, если у вас есть List <String>, который вы хотите отобразить, но если у вас есть List <MyObject>, он не будет вызывать toString () в нем, как того
требует

3
Почему, черт возьми, это не первый (и принятый) ответ ... Мне всегда приходится прокручивать
Джек,

2
Требуется Android API 26+
Арсен

391

Если вы делаете это на 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 пользы мало, но я решил добавить его в эту ветку ...


1
Так как TextUtils.joinпринимает Object[], я предполагаю, что он вызывает toString()на каждом токене. PatientDetailsРеализует ли toString()? Если это не решит проблему, возможно, вы застряли, делая что-то с Separatorклассом.
JJ Geewax

5
org.apache.lang3.StringUtilsделает практически то же самое, так что ваш ответ был абсолютно полезным для меня за пределами Android :)
Патрик

1
Популярность этого ответа (в настоящее время
пользуется

1
Могу поспорить, что вы были отправлены на эту Землю, чтобы решить мою проблему :)
kofoworola

1
Вы спасли мою жизнь. <3
Пратик Бутани

234

Скачайте Apache Commons Lang и используйте метод

 StringUtils.join(list)

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

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

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


63
К сожалению, жители СО предпочитают изобретать велосипед.
Скаффман

33
Также в Apache Commons Lang. Использование будет выглядетьStringUtils.join(list.toArray(),"\t")
Мухд

33
Это конкретное колесо вряд ли стоит дополнительной зависимости.
Сева Алексеев

25
@SevaAlekseyev Я думаю , что спорно. Если вы сразу добавите эту зависимость, вы будете использовать ее классы гораздо чаще, чем нет. Вместо использования "if string! = Null && string.trim ()! =" "Вы будете использовать StringUtils.isNotEmpty ... Вы будете использовать ObjectUtils.equals (), чтобы вам не приходилось везде проверять наличие нуля. Но если вы ждете одну зависимость, которая оправдывает использование этих библиотек, вы, возможно, никогда не будете их использовать
Рави Валлау

6
Также приятно, что в конце не будет добавлена ​​дополнительная вкладка!
keuleJ

139

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

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

2
Это мое любимое решение. : -> +1 для гуавы.
csgeek

Ctrl + F для Guava. Спасибо!
Прииду Нимре

List <String> list = new ArrayList <String> (); list.add ( "Hello"); list.add ( "Хай"); list.add ( "Привет"); System.out.println (list.toString (). Substring (1, list.toString (). Length () - 1) .replaceAll (",", "\ t"));
Лова Читтумури

86

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

Случай 1 . Если в вашем пути к классам есть Apache StringUtils (как в 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()); 

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


1
Я не заметил вашего решения для случая 2, прежде чем опубликовать свое собственное. Ваш на самом деле намного лучше и экономит дополнительный шаг. Я определенно рекомендовал бы это по некоторым ответам, которые, кажется, требуют дополнительных инструментов.
Эллиандр

5
Arrays.toString(myList.toArray())- самое простое и понятное решение
Ондрей Бозек

Я думаю, что это следует рассматривать как лучшее и наиболее эффективное решение
Мори

Arrays.toString(list.toArray())легко написать, но крайне неэффективно.
Матье

60

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

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

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

myList.toString()

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

[стр1, стр2, стр3]

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

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

3
Я думаю, что это на самом деле более эффективно, чем методы, опубликованные выше. Такой позор это показано на дне. Это может быть не элегантно, но я твердо верю, что ваше решение выполняет O (n), в то время как другие теоретически работают в O (n ^ 2) (поскольку строки Java неизменны, вы в основном создаете новую строку при каждом слиянии, и она становится хуже по мере того, как полученная строка становится больше)
user3790827

Если ваш текст очень большой, вы в конечном итоге съедаете ресурсы своего компьютера.
user3790827

Этот метод в 7-8 раз медленнее, чем использование StringBuilder.
Зока

@ user3790827 Вы смотрите на очень поверхностный уровень. Если я скажу вам искать человека в mдомашних хозяйствах в nгородах в xштатах, вы скажете, что это O(mnx)или O(n^3), но я могу перефразировать его, чтобы сказать: «Ищите этого человека в этой стране», и вы сразу же скажете мне O(n). Кроме того, все алгоритмы здесь, как правило O(n), не имеют циклов внутри цикла.
Джай

39

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

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

Спасибо! это то, чем я заканчиваю :)
Juan Besa

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

1
Использование StringBuilder в явном виде представляет собой большую проблему, если вы объединяете, скажем, 50 000 строк, в которых получается строка ~ 1 МБ - это может быть разницей в 1 минуту по сравнению со временем выполнения 1 с.
Мистер Напик

36

Большинство проектов Java часто имеют доступ к Apache-Commons Lang. Методы 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();
}

Параметры:

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

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

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

С: 2.3


2
Это очень хорошо, так как join(...)метод имеет варианты как для массивов, так и для коллекций.
Викингстеве

Мне нужно было создавать коллекции строк с редко заполненными элементами, со случайными числами. В конце мне понадобилась строка, а не массив, а не массив в строку. Это мой код: Collections.shuffle (L); StringBuilder sb = new StringBuilder (); L.forEach (e -> sb.append (e)); return sb.toString ();
Добровое


14

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

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

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

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

13

Элегантный способ справиться с конечными символами разделения - использовать Class Separator

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

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


8

В 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"));

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

1
Это было мое первое впечатление тоже. Но после того, как я привык к этому, я нахожу это потрясающим. Например, вы можете добавить такие операции, как filter. В конце концов, мне намного легче читать код.
Амра

3
В Java 8: String.join ("\ t", массив)
Томаш

Последний комментарий работает, только если содержание Stringтоже. Если это список Integers у вас есть проблемы
LeO

Все 3 из них требует API уровня 24 на Android.
Asad35Waheed

6

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

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

StringBuilder sb = new StringBuilder();

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

String finalString = sb.toString();

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


5

Если вы оказались на 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 () в классе объектов списка.


1
Использование reduceкрайне неэффективно для конкатенации строк. Вы должны сделать это способом Java 8, return StreamSupport.stream(list).map(Object::toString).collect(joining("\n"))который внутренне использует StringJoiner. Нет причин, по которым вы не могли бы сделать это с помощью streamsupport .
Стефан Зобель

Другое дело, что ваш код ужасно потерпит неудачу (из-за Optional#get), если ему передадут пустой список.
Стефан Зобель

@StefanZobel Спасибо за указание на вопросы эффективности и крайних случаев. Модифицированный код.
Джавад Садекзаде

3

Если вы не хотите, чтобы последний \ 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());

3

Код ниже может помочь вам,

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

3

Может быть, не самый лучший, но элегантный способ.

Arrays.deepToString (Arrays.asList ("Test", "Test2")

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
Я думаю, что это лучший способ, если «лучший» означает читабельность кода. Я не смотрел на код, но он, вероятно, столь же эффективен, как и метод StringBuilder (он, вероятно, использует StringBuilder под капотом), хотя и не так настраиваемый в выводе.
Майк Миллер

3

Будет ли следующее хорошо:

List<String> streamValues = new ArrayList<>();
Arrays.deepToString(streamValues.toArray()));   

3

Если вы используете 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 Collections.


3
List<String> stringList = getMyListOfStrings();
StringJoiner sj = new StringJoiner(" ");
stringList.stream().forEach(e -> sj.add(e));
String spaceSeparated = sj.toString()

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


3

В одну строку: с [12,0,1,78,12] до 12 0 1 78 12

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

2
1) Больше нет причин использовать подобный код (по состоянию на 4 года назад!). 2) Этот ответ ничего не добавляет к вопросам и ответам.
Radiodef

5 звезд в одной строке. большое спасибо.
Атеф Фарук

Это работает отлично. Для строки через запятую используйте: String srt = list.toString (). ReplaceAll ("\ [| \]", "");
Asad35Waheed

2

Для разделения с помощью вкладок вместо 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");
}

1

Я вижу довольно много примеров, которые зависят от дополнительных ресурсов, но кажется, что это было бы самое простое решение: (это то, что я использовал в моем собственном проекте), которое в основном просто конвертирует из ArrayList в Array, а затем в List ,

    List<Account> accounts = new ArrayList<>();

   public String accountList() 
   {
      Account[] listingArray = accounts.toArray(new Account[accounts.size()]);
      String listingString = Arrays.toString(listingArray);
      return listingString;
   }

0

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

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

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


0

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

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

-1

Как насчет этой функции:

public static String toString(final Collection<?> collection) {
    final StringBuilder sb = new StringBuilder("{");
    boolean isFirst = true;
    for (final Object object : collection) {
        if (!isFirst)
            sb.append(',');
        else
            isFirst = false;
        sb.append(object);
    }
    sb.append('}');
    return sb.toString();
}

это работает для любого типа коллекции ...

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.