Il modo migliore per convertire un ArrayList in una stringa


353

Ne ho uno ArrayListche voglio produrre completamente come una stringa. In sostanza, voglio emetterlo in ordine usando toStringciascun elemento separato da schede. C'è un modo veloce per farlo? Potresti attraversarlo (o rimuovere ogni elemento) e concatenarlo a una stringa, ma penso che questo sarà molto lento.


4
oppure rimuovi ciascun elemento Fai attenzione, la rimozione di elementi da un elenco ArrayList è un grande NO-NO, poiché il tuo "loop" richiederà un tempo quadratico a causa dello spostamento degli elementi (purché esegui il loop dal primo all'ultimo elemento).
Dimitry K

Risposte:


329

Fondamentalmente, l'uso di un ciclo per scorrere l'iter ArrayListè l'unica opzione:

NON utilizzare questo codice, continua a leggere fino alla fine di questa risposta per vedere perché non è desiderabile e quale codice dovrebbe essere utilizzato invece:

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

In effetti, una concatenazione di stringhe andrà bene, dato che il javaccompilatore ottimizzerà la concatenazione di stringhe come una serie di appendoperazioni su un StringBuildercomunque. Ecco una parte dello smontaggio del bytecode dal forloop dal programma sopra:

   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;

Come si può vedere, il compilatore ottimizza quel loop usando un StringBuilder, quindi le prestazioni non dovrebbero essere una grande preoccupazione.

(OK, a un secondo sguardo, StringBuilderviene istanziato su ogni iterazione del loop, quindi potrebbe non essere il bytecode più efficiente. L'istanza e l'uso di un esplicito StringBuilderprobabilmente produrrebbe prestazioni migliori.)

In effetti, penso che avere qualsiasi tipo di output (sia esso su disco o sullo schermo) sarà almeno di un ordine di grandezza più lento rispetto a doversi preoccupare delle prestazioni delle concatenazioni di stringhe.

Modifica: come sottolineato nei commenti, l'ottimizzazione del compilatore sopra sta effettivamente creando una nuova istanza di StringBuilderogni iterazione. (Che ho notato in precedenza.)

La tecnica più ottimizzata da utilizzare sarà la risposta di Paul Tomblin , in quanto crea un'istanza di un singolo StringBuilderoggetto all'esterno del forloop.

Riscrivere il codice sopra per:

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

Istituirà solo StringBuilderuna volta l'esterno del ciclo e effettuerà solo le due chiamate al appendmetodo all'interno del ciclo, come evidenziato in questo bytecode (che mostra l'istanza di StringBuildere il ciclo):

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

Quindi, in effetti l'ottimizzazione della mano dovrebbe essere più performante, poiché l'interno del forciclo è più breve e non è necessario creare un'istanza StringBuildersu ogni iterazione.


8
Considerare l'utilizzo di una classe StringBuilder invece di aggiungere semplicemente stringhe, per motivi di prestazioni.
Jeremy

5
Il compilatore ottimizzerà la concatenazione di stringhe, ma verrà creato un nuovo oggetto StringBuilder ogni volta che viene eseguito il ciclo.
Pedro Henriques,

4
-1 per l'uso di + =, in questo caso è un killer delle prestazioni. Utilizzare sempre StringBuilder per l'aggiunta ripetuta a una stringa.
Starblue

10
Questa soluzione aggiunge un "\ t" extra alla fine della stringa che è spesso indesiderata, specialmente quando si desidera creare un elenco separato da virgole o simile. Penso che le altre soluzioni pubblicate qui, usando Apache Commons o Guava, siano superiori.
Peter Goetz,

3
Questa non è la risposta migliore. Guarda in basso Vitalii per Javao Geewax perAndroid
Gibolt

895

In Java 8 o versioni successive:

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

Nel caso in cui listnon sia di tipo String, è possibile utilizzare un raccoglitore di join:

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

144
Non riesco a credere che ci sia voluto fino a Java 8 per aggiungere questo.
Paul,

45
Questa risposta dovrebbe essere votata di più! Nessun hack, nessuna libreria e nessun loop.
Songo,

4
questo non funziona per quello che ha detto l'OP. String.join accetta come secondo argomento Iterable <? estende CharSequence>. Così funziona se si dispone di un elenco <String> che si desidera visualizzare, ma se avete List <MyObject> Sarà non chiamare toString () in esso come voluto dal PO
Hilikus

3
Perché diavolo questa non è la prima (e accettata) risposta .. Devo sempre scorrere verso il basso finché non so che è lì .. elegante e conciso
Jack

2
Richiede l'API Android 26+
Arsen Sench il

391

Se ti capita di farlo su Android, c'è una bella utility per questo chiamato TextUtils che ha un .join(String delimiter, Iterable)metodo.

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

Ovviamente non lo uso molto al di fuori di Android, ma ho pensato di aggiungerlo a questa discussione ...


1
Dal momento che TextUtils.joinprende un Object[], suppongo che stia chiamando toString()ogni token. Does PatientDetailsimplementare toString()? Se ciò non risolve il problema, potresti essere bloccato a fare qualcosa con la Separatorclasse.
JJ Geewax,

5
org.apache.lang3.StringUtilsfa praticamente lo stesso, quindi la tua risposta mi è stata di grande utilità al di fuori di Android :)
Patrick,

1
La popolarità di questa risposta (attualmente la più votata) mostra che molte domande su Java derivano dallo sviluppo di Android
Amr H. Abd Elmajeed,

1
Posso scommettere che sei stato mandato su questa Terra per risolvere il mio problema :)
kofoworola il

1
Mi hai salvato la vita. <3
Pratik Butani,

234

Scarica l'Apache Commons Lang e usa il metodo

 StringUtils.join(list)

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

Puoi implementarlo da solo, ovviamente, ma il loro codice è completamente testato ed è probabilmente la migliore implementazione possibile.

Sono un grande fan della libreria Apache Commons e penso anche che sia un'ottima aggiunta alla Java Standard Library.


63
Sfortunatamente, gli abitanti di SO preferiscono reinventare la ruota.
Skaffman,

33
Anche in Apache Commons Lang. L'utilizzo sarebbe simileStringUtils.join(list.toArray(),"\t")
Muhd

33
Questa particolare ruota difficilmente merita una dipendenza aggiuntiva.
Seva Alekseyev,

25
@SevaAlekseyev Penso che sia discutibile. Se aggiungi subito questa dipendenza, utilizzerai le sue classi molto più spesso. Invece di usare "if string! = Null && string.trim ()! =" "Utilizzerai StringUtils.isNotEmpty ... Userai ObjectUtils.equals () in modo da non dover cercare null ovunque. Ma se aspetti la dipendenza che giustifica l'uso di queste librerie, potresti non usarle mai
Ravi Wallau,

6
È anche bello che non aggiungerà una scheda aggiuntiva alla fine!
keuleJ

139

Questa è una domanda piuttosto vecchia, ma immagino che potrei anche aggiungere una risposta più moderna - usa la Joinerclasse di Guava :

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

2
Questa è la mia soluzione preferita : -> +1 per guava.
csgeek,

Ctrl + F'd anche per Guava. Grazie!
Priidu Neemre,

List <String> list = new ArrayList <String> (); list.add ( "Ciao"); list.add ( "Hai"); list.add ( "Ciao"); System.out.println (list.toString (). Substring (1, list.toString (). Length () - 1) .replaceAll (",", "\ t"));
Lova Chittumuri,

86

Cambiare l' elenco in una stringa leggibile e significativa è davvero una domanda comune che tutti possono incontrare.

Caso 1 . Se hai StringUtils di apache nel tuo percorso di classe (come da rogerdpack e Ravi Wallau):

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

Caso 2 . Se vuoi usare solo i modi di JDK (7):

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

Non costruire mai ruote da solo, non usare loop per questo compito su una riga.


1
Non ho notato la soluzione del tuo caso 2 prima di pubblicare la mia. Il tuo è in realtà molto meglio e salva il passaggio extra. Lo consiglierei sicuramente su alcune delle risposte che sembrano richiedere strumenti extra.
Elliander,

5
Arrays.toString(myList.toArray())- la soluzione più semplice e più semplice
Ondrej Bozek,

Penso che questa dovrebbe essere considerata la soluzione migliore e più efficiente
Morey,

Arrays.toString(list.toArray())è facile da scrivere, ma altamente inefficiente.
Matthieu,

60

Se stavi cercando un one-liner veloce, a partire da Java 5 puoi farlo:

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

Inoltre, se il tuo scopo è solo quello di stampare il contenuto e sei meno preoccupato per la "\ t", puoi semplicemente farlo:

myList.toString()

che restituisce una stringa come

[str1, str2, str3]

Se si dispone di un array (non di un array), è possibile ottenere lo stesso risultato in questo modo:

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

3
Penso che questo sia effettivamente più efficiente dei metodi sopra pubblicati. Un vero peccato è mostrato sul fondo. Potrebbe non essere elegante, ma credo fermamente che la tua soluzione esegua O (n) mentre le altre teoricamente funzionano in O (n ^ 2) (poiché le stringhe Java sono immutabili, fondamentalmente crei una nuova stringa su ogni unione, e peggiora come la stringa risultante diventa più grande)
user3790827

Se il tuo testo è molto grande, finisci per mangiare le risorse del tuo computer.
user3790827,

Questo metodo è 7-8 volte più lento rispetto all'uso di StringBuilder.
Zoka,

@ user3790827 Stai osservando un livello molto superficiale. Se ti dico di cercare una persona nelle mfamiglie nelle ncittà degli xStati, diresti che è O(mnx)o O(n^3), ma posso riformularla per dire "cerca questa persona in questo paese" e me lo diresti immediatamente O(n). Inoltre, tutti gli algoritmi qui sono generalmente O(n), non esiste un loop all'interno di un loop.
Jai l'

39

Attraversalo e chiama ToString. Non esiste un modo magico, e se ci fosse, cosa pensi che farebbe sotto le coperte se non attraversarlo? L'unica micro-ottimizzazione sarebbe usare StringBuilder invece di String, e anche questa non è una grande vittoria - concatenare le stringhe si trasforma in StringBuilder sotto le copertine, ma almeno se lo scrivi in ​​questo modo puoi vedere cosa sta succedendo.

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

Grazie! questo è quello che finisco per fare :)
Juan Besa

4
Per le grandi matrici questo non è sicuramente un micro-ottimizzatore. La conversione automatica per utilizzare StringBuilder viene eseguita separatamente per ogni concatenazione di stringhe, il che non aiuta nel caso di un ciclo. Va bene se concateni un gran numero di elementi in una espressione.
Starblue

1
L'uso esplicito di StringBuilder è un grosso problema se si sta concatenando diciamo 50.000 stringhe che producono una stringa di risultati di ~ 1 MB - può essere una differenza di 1 minuto rispetto al tempo di esecuzione di 1 s.
Mr. Napik,

36

La maggior parte dei progetti Java ha spesso disponibile lang apache-commons. I metodi StringUtils.join () sono molto belli e hanno diversi gusti per soddisfare quasi ogni esigenza.

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

parametri:

collection : la raccolta di valori da unire può essere nulla

separatore : il carattere separatore da utilizzare

Restituisce : la stringa unita, null se input iteratore null

Dal: 2.3


2
Questo è molto bello poiché il join(...)metodo ha varianti sia per le matrici che per le raccolte.
vikingsteve,

Avevo bisogno di creare raccolte di stringhe con elementi raramente popolati, con numeri casuali. Alla fine avevo bisogno di una stringa, non di una matrice non di una stringa. Questo è il mio codice: Collections.shuffle (L); StringBuilder sb = new StringBuilder (); L.forEach (e -> sb.append (e)); return sb.toString ();
dobrivoje,


14

Per questo semplice caso d'uso, puoi semplicemente unire le stringhe con una virgola. Se si utilizza Java 8:

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

altrimenti commons-lang ha un metodo join ():

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

13

Un modo elegante per gestire i caratteri di separazione finali è utilizzare il separatore di classe

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

Il metodo toString di Class Separator restituisce il separatore, ad eccezione della prima chiamata. Quindi stampiamo l'elenco senza trascinare (o in questo caso) i separatori iniziali.


8

In Java 8 è semplice. Vedi esempio per l'elenco di numeri interi:

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

O versione multilinea (che è più semplice da leggere):

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

Aggiornamento: una versione più breve

String result = Arrays.asList(1,2,3).stream()
                .map(Object::toString)
                .collect(Collectors.joining("\t"));

2
Questa potrebbe essere solo una riga di codice, ma per me non sembra semplice. Per me attraversare la lista e analizzarne gli elementi nella stringa è più semplice di così.
Bartzilla,

1
Anche questa è stata la mia prima impressione. Ma dopo che mi sono abituato, lo trovo fantastico. Ad esempio è possibile aggiungere operazioni come filter. Alla fine è molto più facile per me leggere il codice.
amra,

3
In Java 8: String.join ("\ t", array)
Tomasz,

L'ultimo commento funziona solo se anche il contenuto è un String. Se si tratta di un elenco di Integers hai un problema
LeO

Tutti e 3 richiedono il livello API 24 su Android.
Asad35Waheed,

6

È un O(n) entrambi i casi algoritmo (a meno che tu non abbia fatto una soluzione multi-thread in cui hai suddiviso l'elenco in più liste secondarie, ma non penso che sia quello che stai chiedendo).

Usa a StringBuildercome di seguito:

StringBuilder sb = new StringBuilder();

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

String finalString = sb.toString();

Il StringBuildersarà molto più veloce di concatenazione di stringhe, perché non sarà ri-un'istanza di un Stringoggetto su ogni concatenazione.


5

Nel caso in cui ti trovi su Android e non stai ancora utilizzando Jack (ad esempio perché manca ancora il supporto per Instant Run) e se desideri un maggiore controllo sulla formattazione della stringa risultante (ad esempio, desideri utilizzare il carattere di nuova riga come il divisore degli elementi), e capita di usare / voler usare la libreria StreamSupport (per usare flussi su Java 7 o versioni precedenti del compilatore), potresti usare qualcosa del genere (ho inserito questo metodo nella mia classe ListUtils):

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

E, naturalmente, assicurati di implementare toString () nella classe degli oggetti della tua lista.


1
L'uso reduceè estremamente inefficiente per la concatenazione di stringhe. Dovresti farlo nel modo Java 8 return StreamSupport.stream(list).map(Object::toString).collect(joining("\n"))che utilizza internamente StringJoiner. Non c'è motivo per cui non sia possibile farlo anche con streamsupport .
Stefan Zobel,

Un'altra cosa è che il tuo codice fallirebbe in modo orribile (a causa di Optional#get) se passasse un elenco vuoto.
Stefan Zobel,

@StefanZobel Grazie per aver sottolineato l'efficienza e le problematiche relative ai casi limite. Modificato il codice
Javad Sadeqzadeh,

3

Se non si desidera l'ultimo \ t dopo l'ultimo elemento, è necessario utilizzare l'indice per verificare, ma ricordare che questo "funziona" (ovvero è O (n)) quando gli elenchi implementano 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

Il codice seguente può aiutarti,

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

Produzione:

Step-1 : [1, 2, 3]
Step-2 : 1, 2, 3

3

Potrebbe non essere il modo migliore, ma elegante.

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

Produzione

[Test, Test2]


1
Penso che questo sia il modo migliore se "migliore" significa leggibilità del codice. Non ho esaminato il codice, ma probabilmente è altrettanto efficace del metodo StringBuilder (probabilmente utilizza un StringBuilder sotto il cofano), sebbene non sia personalizzabile nell'output.
Mike Miller,

3

Sarebbe utile quanto segue:

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

3

Se si utilizzano le raccolte Eclipse , è possibile utilizzare il makeString()metodo

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 riesci a convertire il tuo ArrayListin a FastList, puoi eliminare l'adattatore.

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

Nota: sono un committer per le raccolte Eclipse.


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

Si passa alla new StringJoinersequenza di caratteri che si desidera utilizzare come separatore. Se vuoi fare un CSV:new StringJoiner(", ");


3

In una riga: da [12,0,1,78,12] a 12 0 1 78 12

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

2
1) Non c'è motivo di usare più questo codice (a partire da 4 anni fa!). 2) Questa risposta non aggiunge nulla alle domande e risposte.
Radiodef,

5 stelle in una riga. molte grazie.
Atef Farouk,

Funziona perfettamente. Per la stringa separata da virgola, usare: String srt = list.toString (). ReplaceAll ("\ [| \]", "");
Asad35Waheed,

2

Per separare usando le schede invece di usare println puoi usare 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

Vedo alcuni esempi che dipendono da risorse aggiuntive, ma sembra che questa sarebbe la soluzione più semplice: (che è quello che ho usato nel mio progetto) che sostanzialmente sta semplicemente convertendo da una matrice in una matrice e poi in una lista .

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

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

0

Ormai questa è una vecchia conversazione e i comuni di apache ora usano internamente uno StringBuilder: http://commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line. 3045

Questo, come sappiamo, migliorerà le prestazioni, ma se le prestazioni sono fondamentali, il metodo utilizzato potrebbe essere in qualche modo inefficiente. Considerando che l'interfaccia è flessibile e consentirà un comportamento coerente tra i diversi tipi di raccolta, è in qualche modo inefficace per gli elenchi, che è il tipo di raccolta nella domanda originale.

Baso questo sul fatto che stiamo sostenendo un certo sovraccarico che eviteremmo semplicemente ripetendo gli elementi in un ciclo tradizionale. Invece ci sono alcune cose aggiuntive che accadono dietro le quinte per verificare modifiche simultanee, chiamate di metodi ecc. D'altra parte, il ciclo potenziato per si tradurrà nello stesso overhead poiché l'iteratore viene utilizzato sull'oggetto Iterable (l'Elenco).


0

È possibile utilizzare un Regex per questo. Questo è il più conciso possibile

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

-1

Che ne dici di questa funzione:

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

funziona per qualsiasi tipo di collezione ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.