350 votes

Quelle est la meilleure façon de construire une chaîne d'éléments délimités en Java ?

Alors que je travaillais sur une application Java, j'ai récemment eu besoin d'assembler une liste de valeurs délimitées par des virgules pour la transmettre à un autre service web sans savoir à l'avance combien d'éléments il y aurait. Le meilleur résultat que j'ai pu obtenir de mémoire était quelque chose comme ceci :

public String appendWithDelimiter( String original, String addition, String delimiter ) {
    if ( original.equals( "" ) ) {
        return addition;
    } else {
        return original + delimiter + addition;
    }
}

String parameterString = "";
if ( condition ) parameterString = appendWithDelimiter( parameterString, "elementName", "," );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, "anotherElementName", "," );

Je me rends compte que ce n'est pas particulièrement efficace, puisque des chaînes sont créées un peu partout, mais je cherchais davantage la clarté que l'optimisation.

En Ruby, je peux faire quelque chose comme ceci à la place, ce qui est beaucoup plus élégant :

parameterArray = [];
parameterArray << "elementName" if condition;
parameterArray << "anotherElementName" if anotherCondition;
parameterString = parameterArray.join(",");

Mais comme Java n'a pas de commande de jointure, je n'ai pas trouvé d'équivalent.

Alors, quelle est la meilleure façon de faire cela en Java ?

0 votes

Le StringbBilder est la voie à suivre - java.lang.StringBuilder.

0 votes

Pour Java 8, consultez cette réponse : stackoverflow.com/a/22577623/1115554

605voto

Martin Gladdish Points 1797

Pré Java 8 :

Le langage commons d'Apache est votre ami ici - il fournit une méthode de jointure très similaire à celle que vous utilisez en Ruby :

StringUtils.join(java.lang.Iterable,char)


Java 8 :

Java 8 permet la jonction dès le départ via StringJoiner y String.join() . Les extraits ci-dessous montrent comment vous pouvez les utiliser :

StringJoiner

StringJoiner joiner = new StringJoiner(",");
joiner.add("01").add("02").add("03");
String joinedString = joiner.toString(); // "01,02,03"

String.join(CharSequence delimiter, CharSequence... elements))

String joinedString = String.join(" - ", "04", "05", "06"); // "04 - 05 - 06"

String.join(CharSequence delimiter, Iterable<? extends CharSequence> elements)

List<String> strings = new LinkedList<>();
strings.add("Java");strings.add("is");
strings.add("cool");
String message = String.join(" ", strings);
//message returned is: "Java is cool"

2 votes

Je me demande si cela tient compte du fait que la représentation en chaîne d'un objet de la collection contient le caractère de délimitation lui-même.

4 votes

Exactement ce que je recherchais : StringUtils.join(java.util.Collection,String) du paquet org.apache.commons.lang3.StringUtils , le fichier jar est commons-lang3-3.0.1.jar.

112 votes

Sur Android, vous pouvez également utiliser TextUtils.join().

53voto

Rob Dickerson Points 758

Vous pouvez écrire une petite méthode utilitaire de type jointure qui fonctionne sur les listes java.util.Lists.

public static String join(List<String> list, String delim) {

    StringBuilder sb = new StringBuilder();

    String loopDelim = "";

    for(String s : list) {

        sb.append(loopDelim);
        sb.append(s);            

        loopDelim = delim;
    }

    return sb.toString();
}

Ensuite, utilisez-le comme suit :

    List<String> list = new ArrayList<String>();

    if( condition )        list.add("elementName");
    if( anotherCondition ) list.add("anotherElementName");

    join(list, ",");

3 votes

Pourquoi écrire votre propre méthode si au moins 2 implémentations (apache et guava) existent déjà ?

34 votes

Cela peut, par exemple, être utile si l'on souhaite avoir moins de dépendances externes.

4 votes

J'aime bien comment loopDelim est utilisé à la place de condition. C'est une sorte de hack, mais cela supprime une déclaration conditionnelle d'un cycle. Je préfère encore utiliser l'itérateur et ajouter le premier élément avant le cycle, mais c'est un hack sympa.

49voto

Kevin Points 1723

Dans le cas d'Android, la classe StringUtils de commons n'est pas disponible, donc pour cela j'ai utilisé

android.text.TextUtils.join(CharSequence delimiter, Iterable tokens)

http://developer.Android.com/reference/Android/text/TextUtils.html

31voto

Alex K Points 9115

El La bibliothèque Guava de Google a com.google.common.base.Joiner qui permet de résoudre de telles tâches.

Des échantillons :

"My pets are: " + Joiner.on(", ").join(Arrays.asList("rabbit", "parrot", "dog")); 
// returns "My pets are: rabbit, parrot, dog"

Joiner.on(" AND ").join(Arrays.asList("field1=1" , "field2=2", "field3=3"));
// returns "field1=1 AND field2=2 AND field3=3"

Joiner.on(",").skipNulls().join(Arrays.asList("London", "Moscow", null, "New York", null, "Paris"));
// returns "London,Moscow,New York,Paris"

Joiner.on(", ").useForNull("Team held a draw").join(Arrays.asList("FC Barcelona", "FC Bayern", null, null, "Chelsea FC", "AC Milan"));
// returns "FC Barcelona, FC Bayern, Team held a draw, Team held a draw, Chelsea FC, AC Milan"

Voici un article sur les utilitaires de chaînes de caractères de Guava .

20voto

Vinko Vrsalovic Points 116138

Vous pouvez le généraliser, mais il n'y a pas de jointure en Java, comme vous le dites bien.

Ce site pourrait mieux fonctionner.

public static String join(Iterable<? extends CharSequence> s, String delimiter) {
    Iterator<? extends CharSequence> iter = s.iterator();
    if (!iter.hasNext()) return "";
    StringBuilder buffer = new StringBuilder(iter.next());
    while (iter.hasNext()) buffer.append(delimiter).append(iter.next());
    return buffer.toString();
}

0 votes

Je suis d'accord avec cette réponse mais quelqu'un peut-il modifier la signature pour qu'elle accepte Collection<String> au lieu de AbstractCollection<String> ? Le reste du code devrait être le même mais je pense que AbstractCollection est un détail d'implémentation qui n'a pas d'importance ici.

0 votes

Mieux encore, utilisez Iterable<String> et utiliser simplement le fait que vous pouvez itérer dessus. Dans votre exemple, vous n'avez pas besoin du nombre d'éléments de la collection, ce qui est encore plus général.

1 votes

Ou encore mieux, utilisez Iterable< ? extends Charsequence> et vous pourrez alors accepter des collections de StringBuilders et de Strings et de streams et d'autres choses semblables à des chaînes :)

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