Equivalents Java de C # String.Format () et String.Join ()

111

Je sais que c'est un peu une question pour les débutants, mais y a-t-il des équivalents aux opérations de chaîne de C # en Java?

Plus précisément, je parle de String.Formatet String.Join.

Omar Kooheji
la source
Il y a donc un String.format mais je vais devoir lancer ma propre jointure.
Omar Kooheji
1
Pour join (), j'aime cette réponse: stackoverflow.com/a/6116469/562139
scorpiodawg

Réponses:

92

L'objet Java String a une formatméthode (à partir de 1.5), mais aucune joinméthode.

Pour obtenir un tas de méthodes utilitaires String utiles qui ne sont pas déjà incluses, vous pouvez utiliser org.apache.commons.lang.StringUtils .

Grant Wagner
la source
13
google collections: google-collections.googlecode.com a également un Joiner.
Ron
10
FYI sur le commentaire de Ron, google-collections a été renommé Guava il y a quelque temps.
Kevin Bourrillion
3
Vous devez mettre à jour cette réponse pour indiquer que Java 8 introduit une String.join()méthode.
Duncan Jones
46

String.format . En ce qui concerne la jointure, vous devez écrire le vôtre:

 static String join(Collection<?> s, String delimiter) {
     StringBuilder builder = new StringBuilder();
     Iterator<?> iter = s.iterator();
     while (iter.hasNext()) {
         builder.append(iter.next());
         if (!iter.hasNext()) {
           break;                  
         }
         builder.append(delimiter);
     }
     return builder.toString();
 }

Ce qui précède provient de http://snippets.dzone.com/posts/show/91

Allain Lalonde
la source
6
Plus précisément: StringBuffer pour jdk1.4 et inférieur, StringBuilder pour jdk1.5 et ultérieur, puisque ce dernier n'est pas synchronisé, donc un peu plus rapide.
VonC
2
Au lieu de deux invocations iter.hasNext (), j'ajoute habituellement un delimeter puis "retourne buf.substring (0, buf.length () - delimeter.length ())".
Vilmantas Baranauskas
2
Vous pouvez le rationaliser un peu en quittant tôt pour éviter le délimiteur: while (true) (add_iter; if (! Iter.hasNext ()) break; add_delim;}
13ren
29

À partir de Java 8, join()est désormais disponible sous forme de deux méthodes de classe sur la classe String. Dans les deux cas, le premier argument est le délimiteur.

Vous pouvez transmettre des CharSequences individuels comme arguments supplémentaires :

String joined = String.join(", ", "Antimony", "Arsenic", "Aluminum", "Selenium");
// "Antimony, Arsenic, Alumninum, Selenium"

Ou vous pouvez passer unIterable<? extends CharSequence> :

List<String> strings = new LinkedList<String>();
strings.add("EX");
strings.add("TER");
strings.add("MIN");
strings.add("ATE");

String joined = String.join("-", strings);
// "EX-TER-MIN-ATE"

Java 8 ajoute également une nouvelle classe StringJoiner, que vous pouvez utiliser comme ceci:

StringJoiner joiner = new StringJoiner("&");
joiner.add("x=9");
joiner.add("y=5667.7");
joiner.add("z=-33.0");

String joined = joiner.toString();
// "x=9&y=5667.7&z=-33.0"
qntm
la source
12

Vous pouvez également utiliser des arguments variables pour les chaînes comme suit:

  String join (String delim, String ... data) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < data.length; i++) {
      sb.append(data[i]);
      if (i >= data.length-1) {break;}
      sb.append(delim);
    }
    return sb.toString();
  }
sgsweb
la source
4

En ce qui concerne la jointure, je pense que cela peut sembler un peu moins compliqué:

public String join (Collection<String> c) {
    StringBuilder sb=new StringBuilder();
    for(String s: c)
        sb.append(s);
    return sb.toString();
}

Je ne peux pas utiliser la syntaxe Java 5 autant que je le voudrais (croyez-le ou non, j'utilise 1.0.x ces derniers temps), donc je suis peut-être un peu rouillé, mais je suis sûr que le concept est correct .

modifier l'ajout: les ajouts de chaînes peuvent être lents, mais si vous travaillez sur du code GUI ou sur une routine de courte durée, peu importe que vous preniez .005 secondes ou .006, donc si vous aviez une collection appelée "joinMe" que vous souhaitez ajouter à une chaîne existante "cible", il ne serait pas horrible de simplement insérer ceci:

for(String s : joinMe)
    target += s;

C'est assez inefficace (et une mauvaise habitude), mais vous ne pourrez rien percevoir à moins qu'il y ait des milliers de chaînes ou que ce soit à l'intérieur d'une énorme boucle ou que votre code soit vraiment critique pour les performances.

Plus important encore, il est facile à retenir, court, rapide et très lisible. La performance n'est pas toujours le gagnant automatique dans les choix de conception.

Bill K
la source
La boucle for est correcte, mais vous devez également en faire une Collection <String>. Si vous ne le faites pas, vous devez dire "for (Object o: c)", car vous ne pouvez pas garantir que tout dans c était une chaîne.
Michael Myers
Bon point, je suis encore plus rouillé avec les génériques. Je vais le modifier.
Bill K
Lors de la concaténation en ligne: chaîne + chaîne + chaîne, le compilateur utilise en fait StringBuilder pour ajouter les valeurs. Je me demande si dans la méthode de la boucle for, vous avez le second, si le compilateur ferait de même.
Spencer Kormos
@Spencer K: Il utilise un StringBuilder, mais il en crée un nouveau pour chaque itération (ce n'est pas la méthode la plus efficace, mais alors comment le compilateur est-il censé savoir?). Je ne sais pas si le compilateur JIT pourrait optimiser cela au moment de l'exécution.
Michael Myers
2
Oh, attendez. Parlez-vous + =? Ouais, ça craint. La boucle et l'ajout actuellement dans ma réponse utilisent StringBuilder et c'est ce dont je parle. Bien que + = se soit beaucoup amélioré, vous ne voulez vraiment pas l'utiliser dans une boucle - pas tant qu'il est bogué mais qu'il peut fonctionner comme de la merde (Bug n'est pas un terme généralement utilisé pour les problèmes de performance - du moins pas dans mes 20 ans de programmation). Le JIT peut également améliorer énormément cela - mais je ne me fierais pas à cela.
Bill K
4

Voici une réponse assez simple. Utilisez +=car il s'agit de moins de code et laissez l'optimiseur le convertir en un StringBuilderpour vous. En utilisant cette méthode, vous n'avez pas à faire de vérifications "is last" dans votre boucle (amélioration des performances) et vous n'avez pas à vous soucier de supprimer les délimiteurs à la fin.

        Iterator<String> iter = args.iterator();
        output += iter.hasNext() ? iter.next() : "";
        while (iter.hasNext()) {
            output += "," + iter.next();
        }
Jess
la source
1
Solution très élégante, n'impliquant pas de bibliothèques tierces!
Denis Itskovich
2

Je ne voulais pas importer une bibliothèque Apache entière pour ajouter une simple fonction de jointure, alors voici mon hack.

    public String join(String delim, List<String> destinations) {
        StringBuilder sb = new StringBuilder();
        int delimLength = delim.length();

        for (String s: destinations) {
            sb.append(s);
            sb.append(delim);
        }

        // we have appended the delimiter to the end 
        // in the previous for-loop. Let's now remove it.
        if (sb.length() >= delimLength) {
            return sb.substring(0, sb.length() - delimLength);
        } else {
            return sb.toString();
        }
    }
Martin Konecny
la source
@MrSnowflake Le générateur de chaînes standard a-t-il une méthode "removeCharAt (int index)" Il
n'apparaît
@NSjonas - oui, sa modification de ma réponse est fausse. Le removeCharAtn'existe pas, et la fonction entière ne renvoie plus de chaîne ... Cela résoudra ce problème.
Martin Konecny
pendant que vous y êtes ... cette solution actuelle lancera une "exception d'index hors limites si vous passez dans une liste vide"
NSjonas
fait une modification pour obtenir la bonne longueur si la délimitation est supérieure à 1 caractère. Correction d'un problème où vous coupiez le dernier caractère et non le premier comme vous en aviez vraiment besoin
NSjonas
Merci pour les commentaires. Actualisé.
Martin Konecny
1

Si vous souhaitez joindre (concaténer) plusieurs chaînes en une seule, vous devez utiliser un StringBuilder. C'est bien mieux que d'utiliser

for(String s : joinMe)
    target += s;

Il existe également une légère amélioration des performances par rapport à StringBuffer, car StringBuilder n'utilise pas la synchronisation.

Pour une méthode utilitaire à usage général comme celle-ci, elle sera (éventuellement) appelée plusieurs fois dans de nombreuses situations, vous devez donc la rendre efficace et ne pas allouer de nombreux objets transitoires. Nous avons profilé de très nombreuses applications Java différentes et constatons presque toujours que la concaténation de chaînes et les allocations chaîne / char [] prennent beaucoup de temps / mémoire.

Notre collection réutilisable -> méthode de chaîne calcule d'abord la taille du résultat requis, puis crée un StringBuilder avec cette taille initiale; cela évite de doubler / copier inutilement le caractère interne [] utilisé lors de l'ajout de chaînes.

djb
la source
re: pré-calcul de la taille: quand cela fonctionne, c'est génial. Ce n'est pas toujours possible. Je me souviens avoir lu quelque part que doubler la taille de la mémoire tampon à chaque fois (ou vraiment multiplier par un facteur fixe) donne quelque chose comme la performance n log n, ce qui n'est pas vraiment si mauvais. Dans le cas général, l'alternative est de faire une liste de toutes les chaînes que vous prévoyez de concaténer. Si vous utilisez une ArrayList, vous devez copier à nouveau (à moins que vous ne connaissiez à l'avance la longueur de votre séquence) et si vous utilisez LinkedList, alors il utilise plus de ram et de garbage collection avec les objets de nœud. Parfois, vous ne pouvez pas gagner. Essayez!
Ian
Les exemples / demandes ci-dessus supposaient une collection ou un tableau ordonné de chaînes à rejoindre. De plus, en précalculant la taille, vous évitez les caractères inutilisés supplémentaires que la croissance interne du tableau char [] entraîne généralement.
djb
1

J'ai écrit propre:

public static String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<String> iter = col.iterator();
    if (iter.hasNext())
        sb.append(iter.next().toString());
    while (iter.hasNext()) {
        sb.append(delim);
        sb.append(iter.next().toString());
    }
    return sb.toString();
}

mais Collectionn'est pas pris en charge par JSP, donc pour la fonction de balise, j'ai écrit:

public static String join(List<?> list, String delim) {
    int len = list.size();
    if (len == 0)
        return "";
    StringBuilder sb = new StringBuilder(list.get(0).toString());
    for (int i = 1; i < len; i++) {
        sb.append(delim);
        sb.append(list.get(i).toString());
    }
    return sb.toString();
}

et mettre au .tlddossier:

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
    <function>
        <name>join</name>
        <function-class>com.core.util.ReportUtil</function-class>
        <function-signature>java.lang.String join(java.util.List, java.lang.String)</function-signature>
    </function>
</taglib>

et utilisez-le dans les fichiers JSP comme:

<%@taglib prefix="funnyFmt" uri="tag:com.core.util,2013:funnyFmt"%>
${funnyFmt:join(books, ", ")}
gavenkoa
la source
0

Je vois beaucoup d'implémentations trop complexes de String.Join here. Si vous n'avez pas Java 1.8 et que vous ne souhaitez pas importer une nouvelle bibliothèque, l'implémentation ci-dessous devrait suffire.

public String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    for ( String s : col ) {
        if ( sb.length() != 0 ) sb.append(delim);
        sb.append(s);
    }
    return sb.toString();
}
Brandon Dutton
la source
-1
ArrayList<Double> j=new ArrayList<>; 
j.add(1);
j.add(.92);
j.add(3); 
String ntop=j.toString(); //ntop= "[1, 0.92, 3]" 

Donc, fondamentalement, le String ntop stocke la valeur de la collection entière avec des séparateurs de virgule et des crochets.

Edward Karak
la source
1
Je ne sais pas à quelle partie de la question cela répond? Ce n'est pas un String.format sauf si vous prévoyez d'ajouter les bits de la chaîne bit par bit au tableau, et [1,0.92,3] n'est pas aussi polyvalent qu'une chaîne générique .join.
Omar Kooheji
-8

J'utiliserais simplement l'opérateur de concaténation de chaînes "+" pour joindre deux chaînes. s1 += s2;

Amir Bashir
la source
Parce que c'est une mauvaise pratique et que c'est lent.
MrSnowflake