103 votes

La méthode la plus sophistiquée pour créer des chaînes séparées par des virgules à partir d'une collection, d'un tableau ou d'une liste ?

Au cours de mon travail avec les bases de données, j'ai remarqué que j'écrivais des chaînes de requête et que dans ces chaînes, je devais mettre plusieurs restrictions dans la clause where d'une liste, d'un tableau ou d'une collection. Cela devrait ressembler à ceci :

select * from customer 
where customer.id in (34, 26, ..., 2);

Vous pouvez simplifier cette question en la ramenant à une collection de chaînes de caractères et en voulant créer une liste séparée par des virgules de ces chaînes en une seule chaîne.

L'approche que j'ai utilisée jusqu'à présent est à peu près la même :

String result = "";
boolean first = true;
for(String string : collectionOfStrings) {
    if(first) {
        result+=string;
        first=false;
    } else {
        result+=","+string;
    }
}

Mais comme vous pouvez le constater, c'est très laid. Vous ne pouvez pas voir ce qui se passe au premier coup d'œil, en particulier lorsque les chaînes construites (comme toute requête SQL) deviennent compliquées.

Quelle est votre méthode (plus) élégante ?

0 votes

On peut supposer que le code SQL présenté ci-dessus devrait ressembler à ceci : select * from customer where customer.id in (34, 26, 2) ;

0 votes

Il y a une partie délicate, lorsque les éléments de la liste (chaînes) contiennent eux-mêmes des virgules ou des guillemets doubles et qu'ils doivent être échappés avec des guillemets. Si je n'ai rien oublié, les exemples ci-dessus n'en tiennent pas compte et je déteste l'idée de parcourir en boucle tous les textes et de rechercher les virgules. Pensez-vous qu'il y ait une meilleure façon de résoudre ce problème ?

0 votes

Vérifiez cette réponse... stackoverflow.com/a/15815631/728610

91voto

Julie Points 3850

Utiliser le Google Guava API 's join méthode :

Joiner.on(",").join(collectionOfStrings);

4 votes

Aujourd'hui, la classe s'appelle Joiner ; google-collections.googlecode.com/svn/trunk/javadoc/com/google/

2 votes

Aujourd'hui, les collections sont obsolètes. Utiliser Google Goyave au lieu de cela.

12 votes

Pendant ce temps, org.apache.commons.lang.StringUtils reste inchangé :-)

88voto

gimel Points 30150

Note : Cette réponse était bonne lorsqu'elle a été écrite il y a 11 ans, mais il existe aujourd'hui de bien meilleures options pour faire cela plus proprement en une seule ligne, que ce soit en utilisant uniquement les classes intégrées de Java ou en utilisant une bibliothèque d'utilitaires. Voir les autres réponses ci-dessous.


Les chaînes étant immuables, vous pouvez utiliser la classe StringBuilder si vous avez l'intention de modifier la chaîne dans le code.

La classe StringBuilder peut être considérée comme un objet String mutable qui alloue plus de mémoire lorsque son contenu est modifié.

La proposition initiale de la question peut être rédigée de manière encore plus claire et plus efficace, en prenant soin des éléments suivants virgule de fin redondante :

    StringBuilder result = new StringBuilder();
    for(String string : collectionOfStrings) {
        result.append(string);
        result.append(",");
    }
    return result.length() > 0 ? result.substring(0, result.length() - 1): "";

7 votes

Notez que votre collection doit comporter au moins un élément.

3 votes

0 votes

Voir la correction suggérée pour une liste vide.

79voto

Ogre Psalm33 Points 4886

Je viens de regarder un code qui faisait cela aujourd'hui. Il s'agit d'une variante de la réponse d'AviewAnew.

collectionOfStrings = /* source string collection */;
String csList = StringUtils.join(collectionOfStrings.toArray(), ",");

En StringUtils ( <-- commons.lang 2.x, ou lien commons.lang 3.x ) que nous avons utilisé est celui de Apache Commons .

0 votes

...et d'où vient StringUtils ?

1 votes

Ah, bon point. Cela fait un moment que je n'ai pas regardé ce code, mais je crois que nous utilisions org.apache.commons.lang.StringUtils.

0 votes

Voici un lien direct vers la méthode join de StringUtils commons.apache.org/proper/commons-lang/javadocs/api-release/org/

48voto

La façon dont j'écris cette boucle est la suivante :

StringBuilder buff = new StringBuilder();
String sep = "";
for (String str : strs) {
    buff.append(sep);
    buff.append(str);
    sep = ",";
}
return buff.toString();

Ne vous préoccupez pas de la performance de sep. Une mission est très rapide. Hotspot a tendance à supprimer la première itération d'une boucle de toute façon (car il doit souvent faire face à des bizarreries telles que les contrôles null et mono/bimorphic inlining).

Si vous l'utilisez beaucoup (plus d'une fois), placez-la dans une méthode partagée.

Il y a une autre question sur stackoverflow qui traite de l'insertion d'une liste d'identifiants dans une instruction SQL.

12voto

Miguel Ping Points 9013

J'ai trouvé l'idiome de l'itérateur élégant, parce qu'il a un test pour plus d'éléments (test null/empty omis pour des raisons de brièveté) :

public static String convert(List<String> list) {
    String res = "";
    for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
        res += iterator.next() + (iterator.hasNext() ? "," : "");
    }
    return res;
}

0 votes

... et probablement moins efficace que la solution acceptée, en fonction de la complexité de l'appel à 'hasNext()'. En outre, vous devriez probablement utiliser un StringBuilder plutôt qu'une concaténation de chaînes.

0 votes

OK, si vous voulez être pointilleux sur l'efficacité, utilisez un StringWriter ;)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X