Найкращий спосіб перетворити ArrayList в рядок


353

У мене є ArrayListте, що я хочу вивести повністю як рядка. По суті, я хочу вивести його для того, щоб використовувати toStringкожен елемент, розділений вкладками. Чи є швидкий спосіб зробити це? Ви можете провести цикл (або вилучити кожен елемент) і об'єднати його в String, але я думаю, це буде дуже повільно.


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;

Як видно, компілятор оптимізує цю петлю за допомогою 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петля коротшає, і немає необхідності вводити копії StringBuilderна кожну ітерацію.


8
Подумайте про використання класу StringBuilder замість просто додавання рядків з міркувань продуктивності.
Джеремі

5
Компілятор оптимізує конкатенацію рядків, але ви будете створювати новий об'єкт StringBuilder кожного разу, коли цикл виконується.
Педро Анрікес

4
-1 для використання + =, в цьому випадку це вбивця продуктивності. Завжди використовуйте StringBuilder для повторного додавання до рядка.
starblue

10
Це рішення додає додатковий "\ t" в кінці рядка, який часто небажаний, особливо коли ви хочете створити список, розділений комами або подібний. Я думаю, що інші рішення, розміщені тут, використовуючи Apache Commons або Guava, є вищими.
Пітер Гец

3
Це не найкраща відповідь. Подивіться нижче на Віталія для Javaабо Geewax дляAndroid
Gibolt

895

У Java 8 чи новіших версіях:

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

Якщо listString не типу, може бути використаний приєднувальний колектор:

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

144
Я не можу повірити, що до цього додалося Java 8.
Павло

45
За цю відповідь слід підкреслити більше! Ні хаків, ні бібліотек, ні циклів.
Songo

4
це не працює для того, що сказала ОП. String.join приймає як другий аргумент Iterable <? розширює CharSequence>. Так воно працює, якщо у вас є список <String>, який ви хочете відобразити, але якщо у вас є список <MyObject>, він не зателефонує toString () у ньому, як цього хоче OP
Hilikus

3
Чому чорт це не перша (і прийнята) відповідь .. Мені завжди доводиться прокручуватися вниз, поки я не знаю, що там є .. елегантний і лаконічний
Джек

2
Потрібен Android API 26+
Arsen Sench

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
Популярність цієї відповіді (наразі найбільше проголосованих) свідчить про те, що багато питань щодо Java пов'язані з розробкою Android
Amr H. Abd Elmajeed

1
Я можу поставити під
сумнів, що

1
Ти врятував моє життя. <3
Пратік Бутані

234

Завантажте Apache Commons Lang і використовуйте метод

 StringUtils.join(list)

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

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

Я великий фанат бібліотеки Apache Commons, і я також вважаю, що це чудове доповнення до стандартної бібліотеки Java.


63
На жаль, жителі SO воліють винаходити колесо.
скафман

33
Також в Apache Commons Lang. Використання виглядало б такStringUtils.join(list.toArray(),"\t")
Muhd

33
Це колесо навряд чи варто додаткової залежності.
Сева Алексєєв

25
@SevaAlekseyev Я думаю, що це дискусійно. Якщо ви додасте цю залежність відразу, ви будете використовувати її класи набагато частіше, ніж ні. Замість використання "if string! = Null && string.trim ()! =" "Ви будете використовувати StringUtils.isNotEmpty ... Ви будете використовувати ObjectUtils.equals (), так що вам не доведеться всюди перевіряти наявність null. Але якщо ви дочекаєтесь однієї залежності, яка виправдовує використання цих бібліотек, ви можете ніколи їх фактично не використовувати.
Раві Валлау

6
Також добре, що він не додасть додаткової вкладки в кінці!
keuleJ

139

Це досить старе питання, але я думаю, що я можу також додати більш сучасну відповідь - використовуйте Joinerклас від Guava :

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

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

Ctrl + F'd також для Guava. Дякую!
Priidu Neemre

Список <String> list = новий ArrayList <String> (); list.add ("Привіт"); list.add ("Хай"); list.add ("Привіт"); System.out.println (list.toString (). Substring (1, list.toString (). Length () - 1) .replaceAll (",", "\ t"));
Lova Chittumuri

86

Зміна списку на читабельну та змістовну рядок - це справді поширене питання, з яким може зіткнутися кожен.

Випадок 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()); 

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


1
Я не помітив Вашого рішення Case 2, перш ніж публікувати своє. Ваш насправді набагато кращий і економить додатковий крок. Я б точно рекомендував це над деякими відповідями, які, здається, потребують додаткових інструментів.
Елліандр

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

Я думаю, що це слід вважати найкращим та найефективнішим рішенням
Морей

Arrays.toString(list.toArray())легко написати, але дуже неефективно.
Матьє

60

Якщо ви шукали швидкий однолінійний апарат, то для Java 5 ви можете зробити це:

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

Крім того, якщо ваша мета - просто роздрукувати вміст і вас менше не хвилює питання "\ t", ви можете просто зробити це:

myList.toString()

яка повертає рядок типу

[str1, str2, str3]

Якщо у вас є масив (не ArrayList), ви можете виконати таке ж:

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

3
Я думаю, що це насправді ефективніше, ніж методи, розміщені вище. Такий сором показаний внизу. Це може бути не елегантно, але я впевнений, що ваше рішення працює O (n), тоді як інші теоретично працюють у O (n ^ 2) (оскільки струни Java незмінні, ви в основному створюєте нову струну при кожному злитті, і вона стає гіршою як результат String стає більшим)
користувач3790827

Якщо ваш текст дуже великий, ви, в кінцевому рахунку, їсте ресурси комп'ютерів.
користувач3790827

Цей метод у 7-8 разів повільніше, ніж використання StringBuilder.
Зока

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

39

Проведіть через нього і зателефонуйте доString. Немає чарівного способу, і якби вони були, як ви думаєте, що це робило б під прикриттями, крім того, щоб прошивати його? Єдиною мікрооптимізацією було б використання StringBuilder замість String, і навіть це не величезна виграш - об'єднувальні рядки перетворюються на StringBuilder під обкладинками, але принаймні, якщо ви пишете це так, ви можете побачити, що відбувається.

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

Дякую! це я закінчую робити :)
Хуан Беса

4
Для великих масивів це точно не є мікрооптимізацією. Автоматичне перетворення для використання StringBuilder проводиться окремо для кожного конкатенації рядків, що не допомагає у випадку циклу. Добре, якщо ви об'єднаєте велику кількість елементів в один вираз.
starblue

1
Використання StringBuilder явно є великою справою, якщо ви об'єднуєте, скажімо, 50 000 рядків, що створюють ~ 1 Мб рядок результатів - це може бути різниця в 1 хв порівняно з 1 сек.
Містер Напік

36

У більшості 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 якщо введений null ітератор

З моменту: 2.3


2
Це дуже приємно, оскільки join(...)метод має варіанти як для масивів, так і для колекцій.
vikingsteve

Мені потрібно було створити колекції рядків із рідко заселеними елементами, із випадковими числами. Наприкінці мені знадобився рядок, а не масив, а не масив для рядка. Це мій код: Collections.shuffle (L); StringBuilder sb = новий StringBuilder (); L.forEach (e -> sb.append (e)); повернути sb.toString ();
dobrivoje


14

У цьому простому випадку ви можете просто з'єднати рядки з комою. Якщо ви використовуєте Java 8:

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

в іншому випадку commons-lang має метод join ():

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

13

Елегантний спосіб розібратися із символами розділення - це використання класового роздільника

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

Метод ToString Class 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
Це може бути лише рядок коду, але для мене це виглядає не просто. Для мене проходження списку та аналіз його елементів у рядку простіше, ніж це.
Bartzilla

1
Це теж було моє перше враження. Але після того, як я звикаю, я вважаю це приголомшливим. Наприклад, ви можете додати такі операції, як filter. Зрештою, мені набагато простіше читати код.
амра

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

Останній коментар працює лише в тому випадку, якщо вміст також є String. Якщо його список Integerз вас є проблема
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 і ще не використовуєте Джека (наприклад, тому, що йому все ще не вистачає підтримки для миттєвого запуску), і якщо ви хочете більше контролювати форматування отриманого рядка (наприклад, ви хочете використовувати символ нового рядка як дільник елементів), і, трапляється, використовувати / хочу використовувати бібліотеку 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. Немає причини, щоб ви не могли це зробити також із підтримкою потоку .
Стефан Зобель

Інша справа, що ваш код буде жахливо вийти з ладу (через 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 ("Тест", "Test2")

import java.util.Arrays;

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

Вихідні дані

[Тест, Тест2]


1
Я думаю, що це найкращий спосіб, якщо "кращий" означає читабельність коду. Я не розглядав код, але він, мабуть, такий же ефективний, як метод StringBuilder (він, ймовірно, використовує StringBuilder під кришкою), хоча і не як настроюється у висновку.
Майк Міллер

3

Буде таке корисне:

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

3

Якщо ви використовуєте Eclipse Collection , ви можете скористатися 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.


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

Ви переходите до new StringJoinerпослідовності char, яку ви хочете використовувати як роздільник. Якщо ви хочете зробити 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 (). SubstituAll ("\ [| \]", "");
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 в масив, а потім у список .

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

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

0

Це вже досить давня розмова і апаш-комуни зараз використовують StringBuilder внутрішньо: http://commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line. 3045

Це, як ми знаємо, підвищить ефективність роботи, але якщо продуктивність є критичною, то використаний метод може бути дещо неефективним. Тоді як інтерфейс є гнучким і дозволить послідовно проводити поведінку для різних типів колекції, для списків це дещо неефективно, що є типом колекції в оригінальному питанні.

Я базую це на тому, що ми несемо певні накладні витрати, яких уникнемо, просто перебравшись у елементи, традиційні для циклу. Натомість є деякі додаткові речі, що відбуваються за лаштунками, перевіряючи паралельні модифікації, виклики методів тощо. З іншого боку, посилений цикл призведе до того ж накладних витрат, оскільки ітератор використовується в об'єкті 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.