2009-03-01 5 views
346

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

+4

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

+0

То же самое для коллекций: http://stackoverflow.com/questions/395401/printing-java-collections-nicely-tostring-doesnt-return-pretty-output –

ответ

269

В принципе, с помощью цикла перебрать 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 на каждой итерации. (Это я уже отмечал ранее.)

Самый оптимизированный способ использования будет отвечать на Paul Tomblin, так как он создает экземпляр только объекта 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 короче nd нет необходимости создавать экземпляр StringBuilder на каждой итерации.

+0

Спасибо за ваш ответ. Я не понял, что компилятор, вероятно, оптимизирует его! –

+0

@Juan Besa: Хотя я слышал об этой оптимизации в прошлом, это был первый раз, когда я действительно пытался проверить, действительно ли это происходит (фактически глядя на байт-код), так что это была возможность обучения для меня :) – coobird

+6

По соображениям производительности рассмотрите возможность использования класса StringBuilder вместо просто добавляемых строк. – Jeremy

37

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

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

Спасибо! это то, что я заканчиваю делать :) –

+4

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

+1

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

2

Если каждый элемент имеет нетривиальное строковое представление, и вы хотите вставить вкладки, единственный способ сделать это - это цикл.

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 объекта на каждую конкатенации.

35

Большинство проектов 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(); 
} 

Параметры:

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

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

Возвращает : присоединенная строка, null, если нулевой входной итератор

С: 2.3

+1

Это очень приятно, так как метод 'join (...)' имеет варианты как для массивов, так и для коллекций. – vikingsteve

198

Скачать Jakarta Commons Lang и использовать метод

StringUtils.join(list) 

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

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

+50

К сожалению, обитатели SO предпочитают изобретать велосипед. – skaffman

+31

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

+28

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

1

Если вам не нужен последний \ 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()); 
11

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

0

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

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

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

133

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

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

Приятно, спасибо. Мне нравится Гуава лучше. –

+2

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

+0

Ctrl + F'd для Guava. Благодаря! –

330

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

+0

Как насчет того, содержит ли мой список пользовательский класс? Например, если у меня есть список , где PatientDetails - это мой класс bean, который содержит только getters и seters. Я пробовал эту логику, но получаю classCastException так же, как и я должен использовать ее во время компиляции с помощью (List). Что по вашему мнению является наилучшим решением? – YuDroid

+1

Поскольку 'TextUtils.join' принимает' Object [] ', Я бы предположил что он вызывает 'toString()' на каждом токене. Использует ли 'PatientDetails'' toString() '? Если это не решит проблему, вы можете застрять что-то с классом «Separator». –

+0

'public static String join (разделитель CharSequence, Iterable tokens)' Использование raw-типа в новой библиотеке действительно является ошибкой на стороне Google. Особенно, когда его легко можно было бы обобщить на 'public static String join (разделитель CharSequence, Iterable ). – Natix

6
Arrays.toString (current_array) 
+2

Arrays.toString() ожидает массив. Речь идет о печати списка (массива). См. Комментарий Кена Шиха для улучшения ответа на этот вопрос. – Torsten

+0

Emm .. где проблема? ..))) http://docs.oracle.com/javase/6/docs/api/java/util/List.html#toArray() – Akvel

+1

Да, это также то, что содержится в ответе Кена Ши, как я писал раньше , Я просто указывал, что ваш ответ еще не совсем завершен или не полностью ответил на вопрос об оригинальном плакате. Он не искал способ распечатать массив, но список. Ваш ответ показывает способ печати массива. – Torsten

54

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

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

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

myList.toString() 

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

[str1, str2, str3 ]

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

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

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

+0

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

+0

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

5

ArrayList класс (Java Docs) расширяет AbstractList класс, который расширяет AbstractCollection класс, содержащий метод toString() (Java Docs). Таким образом, вы просто написать

listName.toString(); 

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

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.

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

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

514

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

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

В случае list не из тип String, может использоваться соединительный коллектор:

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

Я не могу поверить, что потребовалось до тех пор, пока Java 8 не добавит этого. – Paul

+28

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

+1

это не работает для того, что сказал ОП. String.join принимает второй аргумент Iterable . Таким образом, он работает, если у вас есть список , который вы хотите отобразить, но если у вас есть список , он будет * не * вызывать toString() в нем по желанию OP – Hilikus

2

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

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 
1

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

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

Выход

[Тест, Test2]

+1

Я думаю, что это лучший способ, если «лучший» означает читаемость кода. Я не смотрел код, но он, вероятно, так же эффективен, как метод StringBuilder (он, вероятно, использует StringBuilder под капотом), хотя и не так настраивается на выходе. –

1

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

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

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

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

+1

Это было мое первое впечатление. Но после того, как я привык к этому, я нахожу это замечательным. Например, вы можете добавлять такие операции, как 'filter'. В конце мне намного легче прочитать код. – amra

+2

В Java 8: String.join ("\ t", array) – Tomasz

62

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

Дело 1.Если у вас есть StringUtils Апача в своем классе пути (как из rogerdpack и Рави Валлау):

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, прежде чем публиковать свои собственные. Ваш на самом деле намного лучше и сохраняет дополнительный шаг. Я определенно рекомендую его по некоторым ответам, которые, как представляется, требуют дополнительных инструментов. – Elliander

+4

'Arrays.toString (myList.toArray())' - самое простое и самое простое решение. –

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

будет следующим ничего хорошего:

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

В случае, если вам случится быть на Android, и вы не используете Джек еще (например, потому что она по-прежнему не хватает поддержки 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-way 'return StreamSupport.stream (list) .map (Object :: toString) .collect (join (" \ n "))', который внутренне использует 'StringJoiner'. Нет причин, по которым вы не могли бы это сделать с помощью [streamsupport] (https://sourceforge.net/projects/streamsupport/). –

+0

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

+0

@StefanZobel Спасибо за то, что вы указали эффективность и проблемы с корпусом. Изменен код. – Metallica

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

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