Melhor maneira de converter um ArrayList em uma string

353

Eu tenho um ArrayListque eu quero produzir completamente como uma String. Essencialmente, quero exibi-lo em ordem, usando o toStringde cada elemento separado por tabulações. Existe alguma maneira rápida de fazer isso? Você pode fazer um loop através dele (ou remover cada elemento) e concatená-lo em uma String, mas acho que isso será muito lento.

Juan Besa
fonte
4
ou remova cada elemento Cuidado, remover elementos de uma lista ArrayList é um grande NÃO-NÃO, pois seu "loop" levará um tempo quadrático devido aos elementos de mudança (desde que você faça loop do primeiro para o último elemento).
precisa
O mesmo para coleções: stackoverflow.com/questions/395401/…
Ciro Santilli, em </

Respostas:

329

Basicamente, o uso de um loop para iterar sobre a ArrayListé a única opção:

NÃO use esse código, continue lendo na parte inferior desta resposta para ver por que não é desejável e qual código deve ser usado:

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

De fato, uma concatenação de strings será ótima, pois o javaccompilador otimizará a concatenação de strings como uma série de appendoperações StringBuilder. Aqui está uma parte da desmontagem do bytecode do forloop do programa acima:

   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;

Como pode ser visto, o compilador otimiza esse loop usando a StringBuilder, portanto, o desempenho não deve ser uma grande preocupação.

(OK, à segunda vista, ele StringBuilderestá sendo instanciado em cada iteração do loop, portanto, pode não ser o bytecode mais eficiente. Instanciar e usar um explícito StringBuilderprovavelmente produziriam melhor desempenho.)

Na verdade, acho que ter qualquer tipo de saída (seja em disco ou na tela) será pelo menos uma ordem de magnitude mais lenta do que ter que se preocupar com o desempenho de concatenações de strings.

Edit: Como apontado nos comentários, a otimização do compilador acima está de fato criando uma nova instância StringBuilderem cada iteração. (O que observei anteriormente.)

A técnica mais otimizada a ser usada será a resposta de Paul Tomblin , pois apenas instancia um único StringBuilderobjeto fora do forloop.

Reescrevendo o código acima para:

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

Instanciará apenas StringBuilderuma vez fora do loop e fará apenas as duas chamadas para o appendmétodo dentro do loop, conforme evidenciado neste bytecode (que mostra a instanciação StringBuildere o loop):

   // 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

Portanto, a otimização manual deve ter um desempenho melhor, pois o interior do forloop é mais curto e não é necessário instanciar um StringBuilderem cada iteração.

coobird
fonte
8
Considere usar uma classe StringBuilder em vez de apenas anexar cadeias, por motivos de desempenho.
Jeremy
5
O compilador otimizará a concatenação de strings, mas você criará um novo objeto StringBuilder cada vez que o loop for executado.
Pedro Henriques
4
-1 para o uso de + =, neste caso, é um matador de desempenho. Sempre use StringBuilder para anexar repetidamente a uma string.
starblue
10
Esta solução adiciona um "\ t" extra no final da string, o que geralmente é indesejável, especialmente quando você deseja criar uma lista separada por vírgula ou similar. Eu acho que as outras soluções postadas aqui, usando Apache Commons ou Guava, são superiores.
22412 Peter Goetz
3
Esta não é a melhor resposta. Veja abaixo em Vitalii Javaou Geewax paraAndroid
Gibolt
895

No Java 8 ou posterior:

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

Caso listnão seja do tipo String, um coletor de junção pode ser usado:

String listString = list.stream().map(Object::toString)
                        .collect(Collectors.joining(", "));
Vitalii Fedorenko
fonte
144
Não acredito que demorou até o Java 8 para adicionar isso.
Paul
45
Esta resposta deve ser votada com mais votos! Sem hacks, sem bibliotecas e sem loops.
Songo
4
isso não funciona para o que o OP disse. String.join assume como segundo argumento Iterável <? estende CharSequence>. Então, ele funciona se você tiver um List <String> que você deseja exibir, mas se você tiver List <MyObject> ele irá não chamar toString () nele como quis pelo OP
hilikus
3
Por que diabos este não é o primeiro (e aceito) resposta .. Eu sempre tem que rolar para baixo até que eu sei que é lá .. elegante e conciso
Jack
2
Requer API Android 26+
Arsen Sench 28/08/17
391

Se você estiver fazendo isso no Android, existe um bom utilitário para isso chamado TextUtils, que possui um .join(String delimiter, Iterable)método.

List<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
String joined = TextUtils.join(", ", list);

Obviamente, não uso muito fora do Android, mas achei que eu o adicionaria a este tópico ...

JJ Geewax
fonte
11
Como TextUtils.joinleva um Object[], eu suponho que ele esteja chamando toString()cada token. Será que PatientDetailsimplementar toString()? Se isso não resolver o problema, você pode ficar preso fazendo algo com a Separatorclasse.
JJ Geewax
5
org.apache.lang3.StringUtilsnão faz praticamente o mesmo, assim que sua resposta era absolutamente de muita utilidade para mim fora do Android :)
Patrick
11
A popularidade desta resposta (atualmente o mais votado um) mostra que muitas perguntas java resultam de desenvolvimento Android
Amr H. Abd Elmajeed
11
Posso apostar que lhe foi enviado a esta terra para resolver o meu problema :)
kofoworola
11
Você salvou minha vida. <3
Pratik Butani 23/11
234

Faça o download do Apache Commons Lang e use o método

 StringUtils.join(list)

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

Você pode implementá-lo sozinho, é claro, mas o código deles é totalmente testado e provavelmente é a melhor implementação possível.

Sou um grande fã da biblioteca Apache Commons e também acho que é um ótimo complemento para a Java Standard Library.

Ravi Wallau
fonte
63
Infelizmente, os habitantes de SO preferem reinventar a roda.
skaffman
33
Também em Apache Commons Lang. O uso seria semelhanteStringUtils.join(list.toArray(),"\t")
Muhd
33
Essa roda em particular dificilmente vale uma dependência extra.
Seva Alekseyev
25
@SevaAlekseyev Eu acho que isso é discutível. Se você adicionar essa dependência imediatamente, fará uso de suas classes com muito mais frequência. Em vez de usar "if string! = Null && string.trim ()! =" "Você usará StringUtils.isNotEmpty ... Você usará ObjectUtils.equals () para não precisar verificar nulo em todos os lugares. Mas se você esperar para a uma dependência que justifica usando essas bibliotecas, você nunca pode realmente usá-los.
Ravi Wallau
6
Também é bom que não adicione uma guia adicional no final!
keuleJ
139

Essa é uma pergunta bastante antiga, mas acho que devo adicionar uma resposta mais moderna - use a Joinerclasse do Guava :

String joined = Joiner.on("\t").join(list);
Jon Skeet
fonte
2
Esta é a minha solução favorita. : -> +1 para goiaba.
csgeek
Ctrl + F'd para Goiaba também. Obrigado!
Priidu Neemre
Lista <> lista = new ArrayList <>> (); list.add ("Olá"); list.add ("Hai"); list.add ("Oi"); System.out.println (list.toString (). Substring (1, list.toString (). Length () - 1) .replaceAll (",", "\ t"));
Lova Chittumuri 15/03/19
86

Mudar a lista para uma string legível e significativa é realmente uma pergunta comum que todos podem encontrar.

Caso 1 . Se você tiver o StringUtils do apache no caminho da sua classe (como no rogerdpack e Ravi Wallau):

import org.apache.commons.lang3.StringUtils;
String str = StringUtils.join(myList);

Caso 2 . Se você deseja usar apenas maneiras do JDK (7):

import java.util.Arrays;
String str = Arrays.toString(myList.toArray()); 

Apenas nunca construa rodas sozinho, não use loop para esta tarefa de uma linha.

Sheng.W
fonte
11
Não notei sua solução do Caso 2 antes de postar a minha. O seu é realmente muito melhor e salva a etapa extra. Eu recomendaria definitivamente algumas das respostas que parecem exigir ferramentas extras.
Elliander
5
Arrays.toString(myList.toArray())- a solução mais fácil e direta
Ondrej Bozek
Eu acho que isso deve ser considerado como o melhor e mais eficiente solução
Morey
Arrays.toString(list.toArray())é fácil de escrever, mas altamente ineficiente.
Matthieu
60

Se você estava procurando uma linha única rápida, a partir do Java 5, você pode fazer isso:

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

Além disso, se seu objetivo é apenas imprimir o conteúdo e estiver menos preocupado com o "\ t", você pode simplesmente fazer o seguinte:

myList.toString()

que retorna uma string como

[str1, str2, str3]

Se você tiver um Array (não ArrayList), poderá fazer o mesmo assim:

 Arrays.toString(myList).replaceAll("\\[|\\]", "").replaceAll(", ","\t")
Ken Shih
fonte
3
Eu acho que isso é realmente mais eficiente que os métodos postados acima. Que pena que é mostrado na parte inferior. Pode não ser elegante, mas acredito firmemente que sua solução executa O (n) enquanto as outras teoricamente são executadas em O (n ^ 2) (como as Java Strings são imutáveis, você basicamente cria uma nova String em todas as mesclagens e fica pior à medida que a String resultante se torna maior)
user3790827
Se o seu texto for muito grande, você acaba consumindo os recursos do computador.
user3790827
Este método é 7-8 vezes mais lento do que usando StringBuilder.
Zoka 8/11
@ user3790827 Você está olhando para um nível muito superficial. Se eu disser para você procurar uma pessoa em mdomicílios nas ncidades dos xestados, você diria que é O(mnx)ou O(n^3), mas posso reformulá-lo para dizer "procure essa pessoa neste país" e você imediatamente me dirá O(n). Além disso, todos os algoritmos aqui são geralmente O(n), não há loop dentro de um loop.
Jai
39

Faça um loop e chame toString. Não existe uma maneira mágica, e se houvesse, o que você acha que estaria fazendo debaixo das cobertas, a não ser percorrê-la? A única micro-otimização seria usar o StringBuilder em vez do String, e mesmo isso não é uma grande vitória - concatenar seqüências se transforma em StringBuilder sob as cobertas, mas pelo menos se você escrever dessa maneira, poderá ver o que está acontecendo.

StringBuilder out = new StringBuilder();
for (Object o : list)
{
  out.append(o.toString());
  out.append("\t");
}
return out.toString();
Paul Tomblin
fonte
Obrigado! é isso que eu acabar fazendo :)
Juan Besa
4
Para matrizes grandes, isso definitivamente não é micro-otimizador. A conversão automática para usar o StringBuilder é feita separadamente para cada concatenação de cadeias, o que não ajuda no caso de um loop. Tudo bem se você concatenar um grande número de elementos em uma expressão.
starblue
11
Usar o StringBuilder explicitamente é um grande problema, se você estiver concatenando, digamos 50.000 strings com ~ 1 MB de string de resultados - pode ser uma diferença de 1 min em comparação com o tempo de execução de 1 s.
Napok
36

A maioria dos projetos Java geralmente tem o apache-commons lang disponível. O método StringUtils.join () é muito bom e possui vários sabores para atender quase todas as necessidades.

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

Parâmetros:

coleção - a coleção de valores para unir pode ser nula

separator - o caractere separador a ser usado

Retorna : a entrada de String unida, nula se nula do iterador

Desde: 2.3

Brian
fonte
2
Isso é muito bom, pois o join(...)método possui variantes para matrizes e coleções.
vikingsteve
Eu precisava criar coleções de strings com elementos raramente preenchidos, com números aleatórios. No final, eu precisava de uma string, não uma matriz, não uma matriz para outra. Este é o meu código: Collections.shuffle (L); StringBuilder sb = novo StringBuilder (); L.forEach (e -> sb.append (e)); retornar sb.toString ();
dobrivoje 19/01/19
14

Para este caso de uso simples, você pode simplesmente juntar as cadeias com vírgula. Se você usa o Java 8:

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

caso contrário, commons-lang possui um método join ():

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

Uma maneira elegante de lidar com caracteres de separação à direita é usar o Class Separator

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

O método toString do Class Separator retorna o separador, exceto a primeira chamada. Assim, imprimimos a lista sem arrastar (ou, neste caso) separadores à esquerda.

akuhn
fonte
8

No Java 8 é simples. Veja o exemplo da lista de números inteiros:

String result = Arrays.asList(1,2,3).stream().map(Object::toString).reduce((t, u) -> t + "\t" + u).orElse("");

Ou versão multilinha (que é mais simples de ler):

String result = Arrays.asList(1,2,3).stream()
    .map(Object::toString)
    .reduce((t, u) -> t + "\t" + u)
    .orElse("");

Atualização - uma versão mais curta

String result = Arrays.asList(1,2,3).stream()
                .map(Object::toString)
                .collect(Collectors.joining("\t"));
amra
fonte
2
Isso pode ser apenas uma linha de código, mas para mim isso não parece simples. Para mim, percorrer a Lista e analisar seus elementos na String é mais simples que isso.
Bartzilla
11
Esta foi a minha primeira impressão também. Mas depois que me acostumei, acho incrível. Por exemplo, você pode adicionar operações como filter. No final, é muito mais fácil ler o código.
Amra
3
No Java 8: String.join ("\ t", array)
Tomasz
O último comentário só funciona se o conteúdo for Stringbom. Se a sua lista de um de Integers você tem um problema
LeO
Todos os três exigem nível API 24 no Android.
Asad35Waheed
6

É um O(n)algoritmo de qualquer maneira (a menos que você tenha criado uma solução multithread em que você dividiu a lista em várias sublistas, mas acho que não é isso que você está pedindo).

Basta usar um StringBuildercomo abaixo:

StringBuilder sb = new StringBuilder();

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

String finalString = sb.toString();

o StringBuilder será muito mais rápido que concatenação porque você não será re-instanciar um Stringobjeto em cada concatenação.

Mike C.
fonte
5

Caso você esteja no Android e ainda não esteja usando o Jack (por exemplo, porque ainda falta suporte para a Execução Instantânea) e se desejar ter mais controle sobre a formatação da sequência resultante (por exemplo, gostaria de usar o caractere de nova linha como o divisor de elementos) e por acaso usar / desejar usar a biblioteca StreamSupport (para usar fluxos no Java 7 ou versões anteriores do compilador), você poderia usar algo assim (eu coloquei esse método na minha classe ListUtils):

public static <T> String asString(List<T> list) {
    return StreamSupport.stream(list)
            .map(Object::toString)
            .collect(Collectors.joining("\n"));
}

E, é claro, certifique-se de implementar toString () na classe dos objetos da lista.

Javad Sadeqzadeh
fonte
11
O uso reduceé extremamente ineficiente para a concatenação de String. Você deve fazer isso da maneira Java 8 return StreamSupport.stream(list).map(Object::toString).collect(joining("\n"))que utiliza internamente StringJoiner. Não há razão para que você também não possa fazer isso com o streamsupport .
Stefan Zobel
Outra coisa é que seu código falharia horrivelmente (devido ao Optional#get) se ele receber uma lista vazia.
Stefan Zobel
@StefanZobel Obrigado por apontar os problemas de eficiência e de ponta. Modificado o código.
Javad Sadeqzadeh 26/09/16
3

Se você não quiser o último \ t após o último elemento, precisará usar o índice para verificar, mas lembre-se de que isso apenas "funciona" (ou seja, é O (n)) quando a lista implementa o 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());

fonte
3

O código abaixo pode ajudá-lo,

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

Resultado:

Step-1 : [1, 2, 3]
Step-2 : 1, 2, 3
Srinivasan.S
fonte
3

Pode não ser o melhor caminho, mas o caminho elegante.

Arrays.deepToString (Arrays.asList ("Teste", "Teste2")

import java.util.Arrays;

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

Resultado

[Teste, Teste2]

Mohan Narayanaswamy
fonte
11
Eu acho que essa é a melhor maneira, se "melhor" significa legibilidade do código. Eu não olhei para o código, mas provavelmente é tão eficiente quanto o método StringBuilder (provavelmente usa um StringBuilder sob o capô), embora não seja tão personalizável na saída.
Mike Miller
3

Seria bom o seguinte:

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

Se você estiver usando o Eclipse Collections , poderá usar o makeString()método

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

Se você pode converter seu ArrayListem um FastList, pode se livrar do adaptador.

Assert.assertEquals(
    "one\ttwo\tthree",
    FastList.newListWith("one", "two", "three").makeString("\t"));

Nota: Sou um colaborador das Coleções Eclipse.

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

Você passa para a new StringJoinersequência char que você deseja que seja usada como separador. Se você deseja fazer um CSV:new StringJoiner(", ");

fmsf
fonte
3

Em uma linha: de [12,0,1,78,12] a 12 0 1 78 12

String srt= list.toString().replaceAll("\\[|\\]|,","");
Pratyush Pranjal
fonte
2
1) Não há mais razão para usar código como este (a partir de 4 anos atrás!). 2) Esta resposta não adiciona nada às perguntas e respostas.
Radiodef
5 estrelas em uma linha. Muito obrigado.
Atef Farouk
Isso funciona perfeitamente. Para string separada por vírgula, use: String srt = list.toString (). ReplaceAll ("\ [| \]", "");
Asad35Waheed em 21/04
2

Para separar usando guias em vez de usar println, você pode usar 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");
}
costeletas
fonte
1

Eu vejo alguns exemplos que dependem de recursos adicionais, mas parece que essa seria a solução mais simples: (que é o que eu usei no meu próprio projeto), que basicamente é apenas a conversão de um ArrayList para um Array e depois para uma Lista .

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

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

Esta é uma conversa bastante antiga e os apache commons agora estão usando um StringBuilder internamente: http://commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line. 3045

Isso, como sabemos, melhora o desempenho, mas se o desempenho é crítico, o método usado pode ser um pouco ineficiente. Embora a interface seja flexível e permita um comportamento consistente entre os diferentes tipos de coleções, é um pouco ineficiente para Listas, que é o tipo de coleção na pergunta original.

Baseamo isso no fato de estarmos incorrendo em alguma sobrecarga que evitaríamos simplesmente iterando os elementos em um loop for tradicional. Em vez disso, há algumas coisas adicionais acontecendo nos bastidores, verificando modificações simultâneas, chamadas de método etc. O loop for aprimorado, por outro lado, resultará na mesma sobrecarga, pois o iterador é usado no objeto Iterável (a Lista).

Alex VI
fonte
0

Você pode usar um Regex para isso. Isso é tão conciso quanto possível

System.out.println(yourArrayList.toString().replaceAll("\\[|\\]|[,][ ]","\t"));
Vikrant Goel
fonte
-1

Que tal esta função:

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

funciona para qualquer tipo de coleção ...

desenvolvedor android
fonte