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

353

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

Хуан Беса
источник
4
или удалите каждый элемент. Будьте осторожны, удаление элементов из списка ArrayList - это большая НЕТ-НЕТ, поскольку ваш «цикл» займет квадратичное время из-за смещения элементов (при условии, что вы выполняете цикл от первого до последнего элемента).
Димитрий К,
То же самое для коллекций: stackoverflow.com/questions/395401/…
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Ответы:

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на каждой итерации.

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

JJ Geewax
источник
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()); 

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

Sheng.W
источник
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 возвращает разделитель, за исключением первого вызова. Таким образом, мы печатаем список без конечных (или в этом случае) ведущих разделителей.

akuhn
источник
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
Srinivasan.S
источник
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()));   
Beezer
источник
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(", ");

fmsf
источник
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");
}
chopss
источник
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;
   }
Elliander
источник
0

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

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

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

Алекс VI
источник
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();
}

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

разработчик Android
источник