IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Concaténation rapide et élégante d'une liste avec séparateur


Sujet :

Java

  1. #1
    Membre Expert Avatar de Ivelios
    Homme Profil pro
    Développeur Java
    Inscrit en
    Juillet 2008
    Messages
    1 031
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 031
    Par défaut Concaténation rapide et élégante d'une liste avec séparateur
    Bonjour,

    Je cherche à trouver la manière la plus simple, rapide et élégante de concacter une list<String> en String avec chaque valeur séparées par un séparateur.

    Voici les 3 solutions que j'ai relevé :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public static String firstConcat(List<String> list, String sep) {
            StringBuilder resultBuilder = new StringBuilder();
            boolean firstValue = true;
            for (String value : list) {
                if (firstValue) {
                    firstValue = false;
                } else {
                    resultBuilder.append(sep);
                }
                resultBuilder.append(value);
            }
            return resultBuilder.toString();
        }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static String secondConcat(List<String> list, String sep) {
            StringBuilder resultBuilder = new StringBuilder();
            for (String value : list) {
                resultBuilder.append(value);
                resultBuilder.append(sep);
            }
            if (resultBuilder.length() == 0) {
                return "";
            } else {
                return resultBuilder.substring(0, resultBuilder.length() - 1);
            }
        }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static String thirdConcat(List<String> list, String sep) {
            StringBuilder resultBuilder = new StringBuilder();
            String mySep = "";
            for (String value : list) {
                resultBuilder.append(mySep);
                resultBuilder.append(value);
                mySep = sep;
            }
            return resultBuilder.toString();
        }
    Un test de performance ne permet pas de privilégié une solution plus qu'une autre, les timers change tout le temps
    J'aimerais avoir votre avis sur la meilleur solution, ou votre propre solution si vous en avez une.

    Merci

    Code de test :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    package sandbox;
     
    import java.util.LinkedList;
    import java.util.List;
     
    public class Concat {
     
        public static void main(String[] args) {
            for (int i = 0; i < 7; i++) {
                test(i*1000000);
            }
        }
     
        public static void test(int nbValues) {
            List<String> list = new LinkedList<>();
            for (int i = 0; i < nbValues; i++) {
                list.add(String.valueOf(i));
            }
     
            System.out.println("nbValues : " + nbValues);
            long time;
     
            time = System.currentTimeMillis();
            firstConcat(list, ",");
            long timerFirstConcat = System.currentTimeMillis() - time;
     
            time = System.currentTimeMillis();
            secondConcat(list, ",");
            long timerSecondConcat = System.currentTimeMillis() - time;
     
            time = System.currentTimeMillis();
            thirdConcat(list, ",");
            long timerThirdConcat = System.currentTimeMillis() - time;
     
            System.out.println("first : " + timerFirstConcat+" | second : " + timerSecondConcat+" | third :  "+ timerThirdConcat);
        }
     
        public static String firstConcat(List<String> list, String sep) {
            StringBuilder resultBuilder = new StringBuilder();
            boolean firstValue = true;
            for (String value : list) {
                if (firstValue) {
                    firstValue = false;
                } else {
                    resultBuilder.append(sep);
                }
                resultBuilder.append(value);
            }
            return resultBuilder.toString();
        }
     
        public static String secondConcat(List<String> list, String sep) {
            StringBuilder resultBuilder = new StringBuilder();
            for (String value : list) {
                resultBuilder.append(value);
                resultBuilder.append(sep);
            }
            if (resultBuilder.length() == 0) {
                return "";
            } else {
                return resultBuilder.substring(0, resultBuilder.length() - 1);
            }
        }
     
        public static String thirdConcat(List<String> list, String sep) {
            StringBuilder resultBuilder = new StringBuilder();
            String mySep = "";
            for (String value : list) {
                resultBuilder.append(mySep);
                resultBuilder.append(value);
                mySep = sep;
            }
            return resultBuilder.toString();
        }
     
    }

  2. #2
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Par défaut
    Par performance, tu entends uniquement temps d'exécution ?
    Attention : une recherche de performance prématurée va entraîner un grand nombre de problèmes.
    Est-ce que cette concaténation est ce qui pose le plus gros problème sur le temps d'exécution global de ton programme ? Si c'est non, laisse tomber et concentre-toi sur la portion de code qui prend le plus de temps.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
    Que la force de la puissance soit avec le courage de ta sagesse.

  3. #3
    Membre Expert
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Par défaut
    En Java 8, String.join(String delimiter, String... elements)


    Donc String.join("-", list)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public String firstConcat(List<String> list, String sep){
         return String.join(sep, list);
    }


    Pour être précis, la signature de méthode est public static String join(CharSequence delimiter, CharSequence... elements), donc on peut lui passer du String, mais aussi du StringBuffer, StringBuilder...

  4. #4
    Membre Expert Avatar de Ivelios
    Homme Profil pro
    Développeur Java
    Inscrit en
    Juillet 2008
    Messages
    1 031
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 031
    Par défaut
    @dinobogan

    Il n'est pas vraiment question de performance, les listes que je traite n'auront que 100 éléments max. Mais je me posais tout de même la question de la performance si je devais l'utiliser à plus grande échelle un jour ou l'autre. Et ça semble être le seule critère fiable pour les comparer

    @eulbobo

    Effectivement, c'est bien vu
    Je n'ai pas encore pris le temps de me mettre à la page java 8

  5. #5
    Modérateur

    Homme Profil pro
    Développeur java, access, sql server
    Inscrit en
    Octobre 2005
    Messages
    2 713
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur java, access, sql server
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 713
    Par défaut
    Si tu peux prendre un profiler (dans NetBeans il y en a un) il te donneras des résultats plus précis (mémoire occupée par exemple)
    Labor improbus omnia vincit un travail acharné vient à bout de tout - Ambroise Paré (1510-1590)

    Consulter sans modération la FAQ ainsi que les bons ouvrages : http://jmdoudoux.developpez.com/cours/developpons/java/

  6. #6
    Membre Expert
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Par défaut
    Citation Envoyé par Ivelios Voir le message
    Effectivement, c'est bien vu
    Je n'ai pas encore pris le temps de me mettre à la page java 8
    Depuis le temps que je l'attendais cette fonction, j'étais content de la voir débarquer en java 8 ^^

    Sinon, sur les projets sur lesquels j'ai participé, j'ai vu les trois forme que tu as présenté plus haut... Au niveau perf, il n'y a pas réellement de différence puisqu'il n'y a pas plus de parcours, plus de test...
    Sauf pour le deuxième qui concatène tout et qui enlève la fin (attention à ne pas avoir un séparateur qui fait plus d'un caractère par contre... Et celle là, tu n'y avais pas pensé vu ton code :p)


    Et pour faire du profiling, il y a plein d'outils avec ton JDK8 comme jVisualVM ou JMC (java mission control)

  7. #7
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,


    Coté performance les différences entre les versions doivent vraiment être minimes... Le principal étant d'utiliser StringBuilder.
    Mais perso si je devais implémenter cela j'ajouterais le premier élément seul, puis les autres dans une boucle.

    Avec un Iterator ca se fait tout seul :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    	public static String concat(Iterable<String> values, String sep) {
    		Iterator<String> iterator = values.iterator();
    		if (iterator.hasNext()) {
    			StringBuilder sb = new StringBuilder();
    			// On ajoute le premier élément :
    			sb.append(iterator.next());
    			while (iterator.hasNext()) {
    				// Puis les suivants précédés du séparateur :
    				sb.append(sep);
    				sb.append(iterator.next());
    			}
    			// Et on renvoit le résultat :
    			return sb.toString();
    		}
    		// Cas particulier : aucun élément
    		return "";
    	}
    En plus j'utiliserais Iterable<String> à la place de List<String>, afin d'ouvrir ma méthode à d'autres types...





    A noter qu'une autre solution consiste à utiliser les Streams :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    	public static String concat(List<String> list, String sep) {
    		return list.stream()
    				.collect(Collectors.joining(sep));
    	}
    Tel quel il y a peu d'intérêt, mais cela a deux avantages :
    • L'API de Stream permet de manipuler facilement les données, si besoin.
    • Le tout peut être paralléliser facilement.




    a++

  8. #8
    Membre Expert Avatar de Ivelios
    Homme Profil pro
    Développeur Java
    Inscrit en
    Juillet 2008
    Messages
    1 031
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 031
    Par défaut
    Bonjour à tous,

    Citation Envoyé par eulbobo
    Sauf pour le deuxième qui concatène tout et qui enlève la fin (attention à ne pas avoir un séparateur qui fait plus d'un caractère par contre... Et celle là, tu n'y avais pas pensé vu ton code :p)
    Effectivement c'est un oubli bête et méchant ça

    Citation Envoyé par adiGuba
    Avec un Iterator ca se fait tout seul
    Je n'y avais pas pensé à celle-ci, merci

    Merci à tous pour vos réponses.
    Conclusion : Pas de solution miracle pour faire le concat en Java 7, il faut faire une fonction utilitaire maison qui sont toutes plus ou moins pareil.
    Conclusion 2 : Passer sous Java 8

  9. #9
    Membre Expert
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Par défaut
    Pour la meme problématique, je me suis orienté sur ta solution 1. La solution 2 me semble moche. La 3 est equivalente à la 1.

    Pour avoir un peu pensé à ca (faut croire que tu n'es pas le seul à te poser des questions métaphysiques aussi importantes), je n'ai pas trouvé mieux non plus. Sinon, la solution d'adiGuba est sympa mais à le désavantage d'avoir 2 niveaux d'imbrication. Je ne parle pas des solutions java 8 / stream, l'interet à mes yeux étant plutot l'adaptation de la logique plutot que l'implementation dans un langage (la question etant aussi valable en c++, c#, ...).

  10. #10
    Membre confirmé
    Homme Profil pro
    Architecte réseau
    Inscrit en
    Mars 2014
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte réseau
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Mars 2014
    Messages : 21
    Par défaut
    Bonjour,

    J'ai souvent rencontré ces problématiques et j'avoue que en utilisant Java 8 et les streams, j'ai trouvé plein d'avantages en terme de compacité de code. On peut faire des choses très pratiques et très efficaces et assez modulaire. Par exemple, l'utilisation de Stream.collect() avec un collector de String permet de partir d'une liste d'Objets quelconques (au lieu d'une liste de String)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    		Collection<Object> objects = new LinkedList<>();
    		String sep = " ; ";
    		String resultat = objects.stream().map(o->o.toString()).collect(Collectors.joining(sep));
    dans ce cas j'ai utilisé toString(), mais on peut choisir de récupérer un nom ou un identifiant si les Objets en questions en ont.

    Après on peut aussi générer son propre Collecteur, ce qui permet de faire des choses plus compliquées (bien que plus dur de gérer l'élément de séparation de façon propre).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    		String autreResultat = objects.stream().map(o->o.toString()).collect(StringBuilder::new,(sb,s)->sb.append(s),(sb1,sb2)->sb1.append(sb2)).toString();
    		String idem = objects.stream().map(o->o.toString()).collect(StringBuilder::new,StringBuilder::append,StringBuilder::append).toString();
    		String idemInverse = objects.stream().map(o->o.toString()).collect(StringBuilder::new,(sb,s)->sb.insert(0,s),StringBuilder::append).toString();
    Où le premier argument est l'initiateur (Supplier), le deuxième argument est la fonction d’agrégation des éléments dans l'objet contenant (ici un StringBuilder) et enfin la dernier argument est l'opération de réduction entre deux streams qui ont été parallélisés. (les 2 premières lignes sont strictement équivalentes)
    Cette definition de son collecteur, permet de faire les choses manuellement et donc de faire des choses un peu différentes, comme au dessus où la dernière instruction inverse l’ordonnancement des Objets.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Struts] Tester la taille d'une List avec un Tag Logic
    Par yolepro dans le forum Struts 1
    Réponses: 5
    Dernier message: 24/08/2007, 10h28
  2. rafraichir une liste avec un <SELECT>
    Par karibouxe dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 20/02/2006, 09h05
  3. Ajout dans une liste avec un bouton
    Par Invité dans le forum Access
    Réponses: 6
    Dernier message: 07/12/2005, 08h27
  4. Créer une liste avec taille inconnue
    Par C_C dans le forum Prolog
    Réponses: 3
    Dernier message: 19/11/2005, 12h46
  5. [MFC] creer une liste avec des check????
    Par ginounet dans le forum MFC
    Réponses: 4
    Dernier message: 16/06/2004, 11h47

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo