Il modo migliore per convertire un ArrayList in una stringa

Ho un ArrayList che voglio produrre completamente come una stringa. In sostanza, voglio esportarlo in ordine usando il toString di ogni elemento separato da tabulazioni. C’è un modo veloce per fare questo? Potresti passarci sopra (o rimuovere ogni elemento) e concatenarlo a una stringa, ma penso che sarà molto lento.

Fondamentalmente, l’uso di un ciclo per iterare su ArrayList è l’unica opzione:

NON usare questo codice, continua a leggere in fondo a questa risposta per capire perché non è desiderabile e quale codice dovrebbe invece essere usato:

 ArrayList list = new ArrayList(); 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, poiché il compilatore javac ottimizzerà comunque la concatenazione di stringhe come una serie di operazioni di append su un StringBuilder . Ecco una parte dello sassembly del bytecode dal ciclo for dal programma di cui sopra:

  61: new #13; //class java/lang/StringBuilder 64: dup 65: invokespecial #14; //Method java/lang/StringBuilder."":()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 seconda vista, StringBuilder viene istanziato su ogni iterazione del ciclo, quindi potrebbe non essere il bytecode più efficiente. L’istanziazione e l’uso di un StringBuilder esplicito probabilmente produrranno 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 di dover preoccuparsi delle prestazioni delle concatenazioni di stringhe.

Modifica: Come sottolineato nei commenti, l’ottimizzazione del compilatore sopra è in effetti la creazione di una nuova istanza di StringBuilder su ogni iterazione. (Che ho notato in precedenza.)

La tecnica più ottimizzata da utilizzare sarà la risposta di Paul Tomblin , poiché istanzia solo un singolo object StringBuilder al di fuori del ciclo for .

Riscrivendo al codice precedente per:

 ArrayList list = new ArrayList(); 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()); 

Crea un’istanza di StringBuilder una volta all’esterno del ciclo e effettua le due chiamate al metodo append all’interno del ciclo, come evidenziato in questo bytecode (che mostra l’istanziazione di StringBuilder e del ciclo):

  // Instantiation of the StringBuilder outside loop: 33: new #8; //class java/lang/StringBuilder 36: dup 37: invokespecial #9; //Method java/lang/StringBuilder."":()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 

Pertanto, l’ottimizzazione della mano dovrebbe essere migliore, poiché l’interno del ciclo for è più corto e non è necessario creare un’istanza di StringBuilder per ogni iterazione.

In Java 8 o versioni successive:

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

Nel caso in cui l’ list non sia di tipo String, è ansible utilizzare un raccoglitore di join:

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

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

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

Ovviamente non serve molto al di fuori di Android, ma ho pensato di aggiungerlo a questo thread …

Scarica Jakarta Commons Lang e usa il metodo

  StringUtils.join(list) 

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

Sono un grande fan della biblioteca di Jakarta Commons e penso anche che sia una grande aggiunta alla Java Standard Library.

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

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

Cambiare la lista in una stringa leggibile e significativa è davvero una domanda comune che ognuno può incontrare.

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

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

Caso 2 . Se si desidera utilizzare solo modi da JDK (7):

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

Non costruisci mai le ruote da solo, non utilizzare il loop per questo compito di una sola riga.

Se stavi cercando un one-liner veloce, come in Java 5 puoi farlo:

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

Inoltre, se il tuo scopo è solo quello di stampare i contenuti e sono meno preoccupati per “\ t”, puoi semplicemente fare questo:

 myList.toString() 

che restituisce una stringa simile

[str1, str2, str3]

Se hai una matrice (non ArrayList), puoi ottenere lo stesso risultato:

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

Passa attraverso e chiama toString. Non c’è un modo magico, e se ci fosse, cosa pensi che farebbe sotto le coperte oltre a farla scorrere? L’unica micro-ottimizzazione sarebbe quella di usare StringBuilder invece di String, e anche quella non è una vittoria enorme – la concatenazione di stringhe diventa StringBuilder sotto le copertine, ma almeno se la 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(); 

La maggior parte dei progetti Java ha spesso lang di apache-commons disponibili. 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:

raccolta – 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

Android ha una class TextUtil che puoi usare http://developer.android.com/reference/android/text/TextUtils.html

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

Un modo elegante per gestire i caratteri di separazione finali è utilizzare Class Separator

 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. In questo modo stampiamo la lista senza separatori in coda (o in questo caso).

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

Basta usare un StringBuilder come di seguito:

 StringBuilder sb = new StringBuilder(); for (Object obj : list) { sb.append(obj.toString()); sb.append("\t"); } String finalString = sb.toString(); 

StringBuilder sarà molto più veloce della concatenazione di stringhe perché non si sta ripetendo l’istanza di un object String su ciascuna concatenazione.

ArrayList class ArrayList ( Java Docs ) estende la class AbstractList , che estende la class AbstractCollection che contiene un metodo toString() ( Java Docs ). Quindi scrivi semplicemente

 listName.toString(); 

Gli sviluppatori Java hanno già capito il modo più efficiente e ti hanno dato questo in un metodo ben confezionato e documentato. Basta chiamare quel metodo.

Per questo semplice caso d’uso, puoi semplicemente unire le stringhe con una virgola. Se usi 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"); 

Se ti capita di essere su Android e non stai ancora usando Jack (ad esempio perché manca ancora il supporto per Instant Run), e se vuoi avere più controllo sulla formattazione della stringa risultante (es. Vorresti usare il carattere di nuova riga come il divisore di elementi), e capita di usare / voler usare la libreria StreamSupport (per usare gli stream su Java 7 o versioni precedenti del compilatore), potresti usare qualcosa di simile (ho messo questo metodo nella mia class ListUtils):

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

E, naturalmente, assicurati di implementare toString () sulla class degli oggetti della lista.

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]

Se si utilizzano raccolte Eclipse , è ansible utilizzare il metodo makeString() .

 ArrayList list = new ArrayList(); list.add("one"); list.add("two"); list.add("three"); Assert.assertEquals( "one\ttwo\tthree", ArrayListAdapter.adapt(list).makeString("\t")); 

Se è ansible convertire ArrayList in FastList , è ansible eliminare l’adattatore.

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

Nota: sono un committer per le raccolte di Eclipse.

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

Si passa al new StringJoiner la sequenza char che si desidera utilizzare come separatore. Se vuoi fare un CSV: new StringJoiner(", ");

Il seguente codice 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 

In Java 8 è semplice. Vedere l’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(""); 

Sarebbe utile il seguente:

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

Se non vuoi l’ultimo \ t dopo l’ultimo elemento, devi usare l’indice per controllare, ma ricorda che questo funziona solo “(cioè è O (n)) quando gli elenchi implementano il RandomAccess.

 List list = new ArrayList(); 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()); 

Per la separazione usando le tabs invece di usare println puoi usare la stampa

  ArrayList mylist = new ArrayList(); 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"); } 

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 è fondamentalmente solo la conversione da un ArrayList a un array e poi a un elenco .

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

Questa è una conversazione abbastanza vecchia ormai e gli Apache Commons ora stanno utilizzando un StringBuilder internamente: http://commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line. 3045

Questo, come sappiamo, migliora le prestazioni, ma se le prestazioni sono critiche, il metodo utilizzato potrebbe essere alquanto inefficiente. Mentre l’interfaccia è flessibile e consente un comportamento coerente tra i diversi tipi di raccolta, è un po ‘inefficiente per gli elenchi, che è il tipo di raccolta nella domanda originale.

Baso questo sul fatto che stiamo incorrendo in un sovraccarico che eviteremmo semplicemente iterando attraverso gli elementi in un ciclo tradizionale. Invece ci sono alcune cose aggiuntive che accadono dietro le scene controllando modifiche simultanee, chiamate di metodo, ecc. Il ciclo for enhanced avrà invece lo stesso overhead poiché l’iteratore è usato sull’object Iterable (l’elenco).

Puoi usare un Regex per questo. Questo è il più conciso ansible

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

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 …

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

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