Utilisation de Java 8 pour convertir une liste d'objets en une chaîne obtenue à partir de la méthode toString ()


206

Il y a beaucoup de nouvelles choses utiles dans Java 8. Par exemple, je peux itérer avec un flux sur une liste d'objets, puis additionner les valeurs d'un champ spécifique des Objectinstances de. Par exemple

public class AClass {
  private int value;
  public int getValue() { return value; }
}

Integer sum = list.stream().mapToInt(AClass::getValue).sum();

Ainsi, je demande s'il existe un moyen de construire un Stringqui concatène la sortie de la toString()méthode à partir des instances sur une seule ligne.

List<Integer> list = ...

String concatenated = list.stream().... //concatenate here with toString() method from java.lang.Integer class

Supposons que cela listcontienne des entiers 1, 2et 3, je suppose que concatenatedc'est "123"ou "1,2,3".


Réponses:


369

Une façon simple consiste à ajouter vos éléments de liste dans un StringBuilder

   List<Integer> list = new ArrayList<>();
   list.add(1);
   list.add(2);
   list.add(3);

   StringBuilder b = new StringBuilder();
   list.forEach(b::append);

   System.out.println(b);

vous pouvez également essayer:

String s = list.stream().map(e -> e.toString()).reduce("", String::concat);

Explication: la carte convertit le flux Integer en flux String, puis sa réduction en tant que concaténation de tous les éléments.

Remarque: c'est normal reductionce qui fonctionne dans O (n 2 )

pour de meilleures performances, utilisez un StringBuilderou mutable reductionsimilaire à la réponse de F. Böller.

String s = list.stream().map(Object::toString).collect(Collectors.joining(","));

Réf: Stream Reduction


2
Ouais, pas une solution en ligne, mais une solution.
mat_boy

n'a pas compris cela. Qu'attendez-vous?
Shail016

2
je ne comprends pas pourquoi "réduction normale qui fonctionne en O (n2)". pour moi ça "ressemble" plus à O (n) ...
datahaki

2
@datahaki Je ne suis pas un Java, mais je suppose que la concaténation (réduction) itérative de chaînes nécessite une (ré) allocation de tableau à chaque itération, ce qui en fait O (n ^ 2). joinde l'autre côté, il faudrait préallouer un seul tableau suffisamment grand pour stocker la chaîne finale avant de la remplir, ce qui la rend O (n).
Eli Korvigo

2
Très bon conseil. Vous pouvez peut-être simplifier en utilisant la notation "::". Ainsi, list.stream().map(Object::toString).reduce("", String::concat). L'utilisation map e-> e.toString()est un peu redondante.
e2a

194

Il y a un collecteur joiningdans l'API. C'est une méthode statique dans Collectors.

list.stream().map(Object::toString).collect(Collectors.joining(","))

Pas parfait à cause de l'appel nécessaire de toString, mais fonctionne. Différents délimiteurs sont possibles.


7
Pour être complet: pour que ce code fonctionne, vous devezimport static java.util.stream.Collectors.joining;
Mahdi

7
Pourquoi avons-nous besoin du mappage et du "toString" explicite? Si le collecteur attend des éléments String, la conversion doit être invoquée implicitement, non?!
Basel Shishani

10

Juste au cas où quelqu'un essaierait de le faire sans java 8, il y a une très bonne astuce. List.toString () renvoie déjà une collection qui ressemble à ceci:

[1,2,3]

Selon vos besoins spécifiques, cela peut être post-traité à votre guise tant que vos éléments de liste ne contiennent pas [] ou,.

Par exemple:

list.toString().replace("[","").replace("]","") 

ou si vos données peuvent contenir des crochets, ceci:

String s=list.toString();
s = s.substring(1,s.length()-1) 

vous obtiendrez une sortie assez raisonnable.

Un élément de tableau sur chaque ligne peut être créé comme ceci:

list.toString().replace("[","").replace("]","").replaceAll(",","\r\n")

J'ai utilisé cette technique pour créer des info-bulles html à partir d'une liste dans une petite application, avec quelque chose comme:

list.toString().replace("[","<html>").replace("]","</html>").replaceAll(",","<br>")

Si vous avez un tableau, commencez par Arrays.asList (list) .toString () à la place

Je suis totalement conscient du fait que ce n'est pas optimal, mais ce n'est pas aussi inefficace que vous pourriez le penser et c'est assez simple à lire et à comprendre. Il est cependant assez rigide - en particulier, n'essayez pas de séparer les éléments avec replaceAll si vos données peuvent contenir des virgules et utilisez la version de la sous-chaîne si vous avez des crochets dans vos données, mais pour un tableau de nombres, c'est à peu près parfait.


Bien que cela fonctionne généralement, ce n'est pas garanti - Listn'applique pas la structure de toString(). AbstractCollectioncependant, utilise cette structure par défaut, et je pense que toutes les Listimplémentations à usage général de Java SE le font également. À titre d'exemple de celui qui ne le fait pas, org.springframework.util.AutoPopulatingListdans Spring ne l'implémente pas toString()et retournerait donc par exemple " org.springframework.util.AutoPopulatingList@170a1".
M. Justin

Arrays.toString()serait un meilleur choix que Arrays.asList(list).toString(), comme il est défini pour renvoyer une chaîne équivalente, il est plus concis et ne nécessite pas de création d'objet supplémentaire.
M. Justin

@ M.Justin Pas un mauvais point pour la partie tableau de cette réponse, mais qu'en est-il d'une simple "liste"? Vous ne pouvez pas vraiment utiliser Arrays.toString () là-dessus. Comment suggéreriez-vous d'utiliser cette technique sur quelque chose comme l'AutoPopulatingList que vous avez mentionné? Peut-être: nouveau ArrayList (autoPopulatingList) .toString ()? Ou je suppose que vous pouvez le convertir en tableau. Peut-être que si vous rencontrez un tel problème, vous devriez espérer que vous êtes sur la version 1.8 ou ultérieure et que vous pourriez utiliser l'une des autres réponses dans ce fil ...
Bill K

5

Les autres réponses sont bonnes. Cependant, vous pouvez également passer Collectors.toList () en tant que paramètre à Stream.collect () pour renvoyer les éléments en tant que ArrayList.

System.out.println( list.stream().map( e -> e.toString() ).collect( toList() ) );

Si vous utilisez System.out.print(ln), (list)il utilisera la toString()méthode des éléments pour imprimer la liste. Donc, ce morceau de code ne fait que répéter ce qui se passe dans la toStringliste.
Shirkam

1
Doit être Collectors.toList () sauf si vous importez de l'électricité statique.
mwieczorek

Oui ... J'ai importé de l'électricité statique pour l'exemple. J'ai déclaré "Collectors.toList ()" dans le corps de la réponse ...;)
Eddie B

2

StringListName = ObjectListName.stream (). Map (m -> m.toString ()) .collect (Collectors.toList ());


2

Il existe une méthode dans l'API String pour les cas d'utilisation de la "liste de chaînes de jonction", vous n'avez même pas besoin de Stream.

List<String> myStringIterable = Arrays.asList("baguette", "bonjour");

String myReducedString = String.join(",", myStringIterable);

// And here you obtain "baguette,bonjour" in your myReducedString variable

1
List<String> list = Arrays.asList("One", "Two", "Three");
    list.stream()
            .reduce("", org.apache.commons.lang3.StringUtils::join);

Ou

List<String> list = Arrays.asList("One", "Two", "Three");
        list.stream()
                .reduce("", (s1,s2)->s1+s2);

Cette approche vous permet également de créer un résultat de chaîne à partir d'une liste d'objets Exemple

List<Wrapper> list = Arrays.asList(w1, w2, w2);
        list.stream()
                .map(w->w.getStringValue)
                .reduce("", org.apache.commons.lang3.StringUtils::join);

Ici, la fonction de réduction vous permet d'avoir une valeur initiale à laquelle vous souhaitez ajouter une nouvelle chaîne Exemple:

 List<String> errors = Arrays.asList("er1", "er2", "er3");
            list.stream()
                    .reduce("Found next errors:", (s1,s2)->s1+s2);

1

Tester les deux approches suggérées dans Shail016 et bpedroso answer ( https://stackoverflow.com/a/24883180/2832140 ), le simple StringBuilder+ append(String)dans une forboucle, semble s'exécuter beaucoup plus rapidement que list.stream().map([...].

Exemple: Ce code parcourt Map<Long, List<Long>>une build une chaîne json, en utilisant list.stream().map([...]:

if (mapSize > 0) {
    StringBuilder sb = new StringBuilder("[");

    for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {

        sb.append("{\"" + entry.getKey().toString() + "\":[");
        sb.append(entry.getValue().stream().map(Object::toString).collect(Collectors.joining(",")));
    }
    sb.delete(sb.length()-2, sb.length());
    sb.append("]");
    System.out.println(sb.toString());
}

Sur ma machine virtuelle de développement, junit prend généralement entre 0,35 et 1,2 seconde pour exécuter le test. Alors qu'en utilisant ce code suivant, cela prend entre 0,15 et 0,33 seconde:

if (mapSize > 0) {
    StringBuilder sb = new StringBuilder("[");

    for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {

        sb.append("{\"" + entry.getKey().toString() + "\":[");

        for (Long tid : entry.getValue()) {
            sb.append(tid.toString() + ", ");
        }
        sb.delete(sb.length()-2, sb.length());
        sb.append("]}, ");
    }
    sb.delete(sb.length()-2, sb.length());
    sb.append("]");
    System.out.println(sb.toString());
}

0

Pouvons-nous essayer cela.

public static void main(String []args){
        List<String> stringList = new ArrayList<>();
        for(int i=0;i< 10;i++){
            stringList.add(""+i);
        }
        String stringConcated = String.join(",", stringList);
        System.out.println(stringConcated);

    }

0
String actual = list.stream().reduce((t, u) -> t + "," + u).get();

7
Habituellement, c'est une bonne idée d'ajouter une explication à votre message qui explique comment fonctionne le code. Cela permet aux nouveaux développeurs de comprendre ce que fait le code.
Caleb Kleveter

1
@CalebKleveter réduit un Listà un seul Stringet sépare chaque élément par une virgule ( ,).
Joe Almore

2
Votre solution fonctionne UNIQUEMENT si list est une List <String>. OP n'a pas spécifié une telle classe. Dans sa question, même une liste <Integer> est pointée. Votre code ne se compile pas dans ce cas.
RubioRic

0

Je vais utiliser l'API Streams pour convertir un flux d'entiers en une seule chaîne. Le problème avec certaines des réponses fournies est qu'elles produisent un runtime O (n ^ 2) en raison de la construction de String. Une meilleure solution consiste à utiliser un StringBuilder, puis à joindre les chaînes ensemble comme étape finale.

//              Create a stream of integers 
    String result = Arrays.stream(new int[]{1,2,3,4,5,6 })                
            // collect into a single StringBuilder
            .collect(StringBuilder::new, // supplier function
                    // accumulator - converts cur integer into a string and appends it to the string builder
                    (builder, cur) -> builder.append(Integer.toString(cur)),
                    // combiner - combines two string builders if running in parallel
                    StringBuilder::append) 
            // convert StringBuilder into a single string
            .toString();

Vous pouvez pousser ce processus un peu plus loin en convertissant la collection d'objets en une seule chaîne.

// Start with a class definition
public static class AClass {
    private int value;
    public int getValue() { return value; }
    public AClass(int value) { this.value = value; }

    @Override
    public String toString() {
        return Integer.toString(value);
    }
}

// Create a stream of AClass objects
        String resultTwo = Arrays.stream(new AClass[]{
                new AClass(1),
                new AClass(2),
                new AClass(3),
                new AClass(4)
        })
                // transform stream of objects into a single string
                .collect(StringBuilder::new,
                        (builder, curObj) -> builder.append(curObj.toString()),
                        StringBuilder::append
                )
            // finally transform string builder into a single string
            .toString();

-1

Avec Java 8+

String s = Arrays.toString(list.stream().toArray(AClass[]::new));

Pas le plus efficace, mais c'est une solution avec une petite quantité de code.


-2

Vous pouvez aussi faire comme ça.

    List<String> list = Arrays.asList("One", "Two", "Three");
    String result = String.join(", ", list);
    System.out.println(result);

Votre solution fonctionne UNIQUEMENT si list est une List <String>. OP n'a pas spécifié une telle classe. Dans sa question, même une liste <Integer> est pointée.
RubioRic
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.