J'ai un ArrayList
que je veux sortir complètement en tant que chaîne. Essentiellement, je veux le sortir dans l'ordre en utilisant le toString
de chaque élément séparé par des tabulations. Y at-il un moyen rapide de faire cela? Vous pouvez le parcourir en boucle (ou supprimer chaque élément) et le concaténer en une chaîne, mais je pense que cela sera très lent.
Fondamentalement, utiliser une boucle pour parcourir le ArrayList
est la seule option:
N'UTILISEZ PAS ce code, continuez de lire au bas de cette réponse pour voir pourquoi ce n'est pas souhaitable et indiquez le code à utiliser à la place:
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);
En fait, une concaténation de chaîne va très bien se passer, car le compilateur javac
optimisera la concaténation de chaîne comme une série d'opérations append
sur un StringBuilder
de toute façon. Voici une partie du désassemblage du bytecode de la boucle for
du programme ci-dessus:
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;
Comme on peut le constater, le compilateur optimise cette boucle à l’aide de StringBuilder
. Les performances ne devraient donc pas être un gros problème.
(OK, au deuxième regard, la StringBuilder
est instanciée à chaque itération de la boucle, de sorte que ce n'est peut-être pas le bytecode le plus efficace. Instancier et utiliser un StringBuilder
explicite donnerait probablement de meilleures performances.)
En fait, je pense qu'avoir une sorte de sortie (que ce soit sur disque ou à l'écran) sera au moins d'un ordre de grandeur plus lent que d'avoir à se soucier de la performance des concaténations de chaînes.
Edit: Comme indiqué dans les commentaires, l'optimisation ci-dessus du compilateur crée une nouvelle instance de StringBuilder
à chaque itération. (Ce que j'ai noté précédemment.)
La technique la plus optimisée à utiliser sera la réponse de Paul Tomblin , car elle instancie un seul objet StringBuilder
en dehors de la boucle for
.
Réécrire dans le code ci-dessus pour:
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());
Instanciera uniquement la StringBuilder
une fois en dehors de la boucle et ne fera que les deux appels à la méthode append
à l'intérieur de la boucle, comme le montre ce bytecode (qui montre l'instanciation de StringBuilder
et la boucle):
// 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
Donc, effectivement, l'optimisation de la main devrait être plus performante, car l'intérieur de la boucle for
est plus court et il n'est pas nécessaire d'instancier un StringBuilder
à chaque itération.
Dans Java 8 ou version ultérieure:
String listString = String.join(", ", list);
Si la list
n'est pas de type String, un collecteur de jonction peut être utilisé:
String listString = list.stream().map(Object::toString)
.collect(Collectors.joining(", "));
Si vous faites cela sur Android, il existe un utilitaire Nice appelé TextUtils qui possède une méthode .join(String delimiter, Iterable)
.
List<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
String joined = TextUtils.join(", ", list);
Évidemment, pas beaucoup d'utilisation en dehors d'Android, mais j'ai décidé de l'ajouter à ce fil ...
Téléchargez Apache Commons Lang et utilisez la méthode
StringUtils.join(list)
StringUtils.join(list, ", ") // 2nd param is the separator.
Vous pouvez bien sûr l'implémenter vous-même, mais leur code est entièrement testé et constitue probablement la meilleure implémentation possible.
Je suis un grand fan de la bibliothèque Apache Commons et je pense aussi que c'est un excellent ajout à la bibliothèque standard Java.
Changer Liste en une lisible et explicite String est vraiment une question courante que tout le monde peut rencontrer.
Cas 1. Si vous avez StringUtils d'Apache dans votre chemin de classe (comme dans rogerdpack et Ravi Wallau):
import org.Apache.commons.lang3.StringUtils;
String str = StringUtils.join(myList);
Cas 2. Si vous souhaitez uniquement utiliser les méthodes de JDK (7):
import Java.util.Arrays;
String str = Arrays.toString(myList.toArray());
Ne construisez jamais de roues par vous-même, n’utilisez pas de boucle pour cette tâche d’une ligne.
Si vous cherchez un one-liner rapide, à partir de Java 5, vous pouvez le faire:
myList.toString().replaceAll("\\[|\\]", "").replaceAll(", ","\t")
De plus, si votre but est simplement d'imprimer le contenu et que vous vous inquiétez moins du "\ t", vous pouvez simplement faire ceci:
myList.toString()
qui retourne une chaîne comme
[str1, str2, str3]
Si vous avez un tableau (pas de tableau), vous pouvez accomplir la même chose comme ceci:
Arrays.toString(myList).replaceAll("\\[|\\]", "").replaceAll(", ","\t")
Faites une boucle et appelez toString. Il n'y a pas de moyen magique, et s'il y en avait, que pensez-vous que cela ferait sous les couvertures autres que de le parcourir en boucle? La seule micro-optimisation consisterait à utiliser StringBuilder à la place de String, et même si ce n’est pas un gain énorme - la concaténation de chaînes se transforme en StringBuilder sous la couverture, mais au moins si vous l’écrivez de cette façon, vous pouvez voir ce qui se passe.
StringBuilder out = new StringBuilder();
for (Object o : list)
{
out.append(o.toString());
out.append("\t");
}
return out.toString();
La plupart des projets Java disposent souvent d'Apache-commons lang. Les méthodes de StringUtils.join () sont très jolies et ont plusieurs saveurs pour répondre à presque tous les besoins.
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();
}
Paramètres:
collection - la collection de valeurs à associer peut être nulle
separator - le séparateur à utiliser
Retourne: la chaîne jointe, null si entrée itérateur NULL
Depuis: 2.3
Android a une classe TextUtil que vous pouvez utiliser http://developer.Android.com/reference/Android/text/TextUtils.html
String implode = TextUtils.join("\t", list);
Pour ce cas d'utilisation simple, vous pouvez simplement joindre les chaînes avec une virgule. Si vous utilisez Java 8:
String csv = String.join("\t", yourArray);
sinon, commons-lang a une méthode join ():
String csv = org.Apache.commons.lang3.StringUtils.join(yourArray, "\t");
Une manière élégante de traiter les caractères de séparation finaux est d'utiliser séparateur de classe
StringBuilder buf = new StringBuilder();
Separator sep = new Separator("\t");
for (String each: list) buf.append(sep).append(each);
String s = buf.toString();
La méthode toString de Class Separator renvoie le séparateur, à l'exception de pour le premier appel. Ainsi, nous imprimons la liste sans séparer (ou dans ce cas) les séparateurs.
ArrayList
class ( Java Docs ) étend AbstractList
class, qui étend AbstractCollection
class qui contient une méthode toString()
( Java Docs ). Donc, vous écrivez simplement
listName.toString();
Les développeurs Java ont déjà compris le moyen le plus efficace et vous l’ont fourni avec une méthode bien emballée et documentée. Appelez simplement cette méthode.
C'est un algorithme O(n)
de toute façon (à moins que vous n'ayez utilisé une solution multi-thread où vous avez divisé la liste en plusieurs sous-listes, mais je ne pense pas que ce soit ce que vous demandez).
Utilisez juste un StringBuilder
comme ci-dessous:
StringBuilder sb = new StringBuilder();
for (Object obj : list) {
sb.append(obj.toString());
sb.append("\t");
}
String finalString = sb.toString();
La StringBuilder
sera beaucoup plus rapide que la concaténation de chaîne car vous ne réinstancierez pas un objet String
à chaque concaténation.
Si vous vous trouvez sur Android et que vous n'utilisez pas encore Jack (p. Ex., Car la prise en charge d'Instant Run n’a toujours pas été prise en charge), et si vous souhaitez davantage de contrôle sur le formatage de la chaîne résultante (par exemple, vous voudriez utiliser le caractère de nouvelle ligne comme diviseur d’éléments), et utiliser/vouloir utiliser la bibliothèque StreamSupport (pour utiliser des flux sur Java 7 ou des versions antérieures du compilateur), vous pourriez utiliser quelque chose comme ceci (J'ai mis cette méthode dans ma classe ListUtils):
public static <T> String asString(List<T> list) {
return StreamSupport.stream(list)
.map(Object::toString)
.collect(Collectors.joining("\n"));
}
Et bien sûr, veillez à implémenter toString () sur la classe de vos objets liste.
En Java 8, c'est simple. Voir exemple de liste d'entiers:
String result = Arrays.asList(1,2,3).stream().map(Object::toString).reduce((t, u) -> t + "\t" + u).orElse("");
Ou version multiligne (plus simple à lire):
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)
.collect(Collectors.joining("\t"));
Est-ce que ce qui suit pourrait être bon:
List<String> streamValues = new ArrayList<>();
Arrays.deepToString(streamValues.toArray()));
Si vous ne voulez pas le dernier\t après le dernier élément, vous devez utiliser l'index pour vérifier, mais rappelez-vous que cela ne fait que "fonctionner" (c'est-à-dire que O(n)) lorsque les listes implémentent le Accès aléatoire.
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());
List<String> stringList = getMyListOfStrings();
StringJoiner sj = new StringJoiner(" ");
stringList.stream().forEach(e -> sj.add(e));
String spaceSeparated = sj.toString()
Vous transmettez au new StringJoiner
la séquence de caractères que vous voulez utiliser comme séparateur. Si vous voulez faire un CSV: new StringJoiner(", ");
Le code ci-dessous peut vous aider,
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);
Sortie:
Step-1 : [1, 2, 3]
Step-2 : 1, 2, 3
Peut-être pas la meilleure façon, mais élégante.
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()));
}
}
Sortie
[Test, Test2]
Si vous utilisez Eclipse Collections , vous pouvez utiliser la méthode 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"));
Si vous pouvez convertir votre ArrayList
en FastList
, vous pouvez vous débarrasser de l'adaptateur.
Assert.assertEquals(
"one\ttwo\tthree",
FastList.newListWith("one", "two", "three").makeString("\t"));
Remarque: je suis un partisan des collections Eclipse.
Pour séparer en utilisant des tabulations au lieu d'utiliser println vous pouvez utiliser 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");
}
Je vois pas mal d'exemples qui dépendent de ressources supplémentaires, mais il semble que ce serait la solution la plus simple: (c'est ce que j'ai utilisé dans mon propre projet), qui consiste essentiellement à convertir un tableau ArrayList en tableau puis en liste. .
List<Account> accounts = new ArrayList<>();
public String accountList()
{
Account[] listingArray = accounts.toArray(new Account[accounts.size()]);
String listingString = Arrays.toString(listingArray);
return listingString;
}
En une ligne: de [12,0,1,78,12] à 12 0 1 78 12
String srt= list.toString().replaceAll("\\[|\\]|,","");
La conversation est maintenant assez ancienne et Apache commons utilise désormais un StringBuilder en interne: http://commons.Apache.org/lang/api/src-html/org/Apache/commons/lang/StringUtils. html # line.3045
Comme nous le savons, cela améliorera les performances, mais si les performances sont critiques, la méthode utilisée risque d’être quelque peu inefficace. Alors que l'interface est flexible et permet un comportement cohérent entre différents types de collection, elle est quelque peu inefficace pour les listes, qui est le type de collection de la question d'origine.
Je fonde ceci sur le fait que nous encourons des frais généraux que nous éviterions en itérant simplement à travers les éléments d’une boucle for traditionnelle. Au lieu de cela, certaines choses supplémentaires se produisent en coulisse, la vérification des modifications simultanées, des appels de méthode, etc. La boucle for améliorée entraînera le même temps système car l'itérateur est utilisé sur l'objet Iterable (la liste).
Vous pouvez utiliser un regex pour cela. C'est aussi concis que possible
System.out.println(yourArrayList.toString().replaceAll("\\[|\\]|[,][ ]","\t"));