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

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

28 ответов


в основном, используя цикл для итерации по 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 вероятно, даст лучшую производительность.)

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

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


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

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

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

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

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


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

 StringUtils.join(list)

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

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


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

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

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

корпус 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()); 

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


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

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

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

myList.toString()

, которая возвращает строку типа

[str1 выглядит следующим образом, стр2, стр3]

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

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

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

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

большинство проектов 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

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

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

С: 2.3


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

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

элегантный способ справиться с конечными символами разделения-использовать Разделитель Класс

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

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


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

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

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

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

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

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

StringBuilder sb = new StringBuilder();

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

String finalString = sb.toString();

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


ArrayList класса (Java Docs) расширяет AbstractList класс, который расширяет AbstractCollection класс, который содержит toString() метод (Java Docs). Поэтому вы просто пишете

listName.toString();

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


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


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

массивы.deepToString (массивы.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]


если вы используете Коллекции Eclipse можно использовать 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.


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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


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

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