Java: convertir la liste <String> en une chaîne


596

JavaScript a Array.join()

js>["Bill","Bob","Steve"].join(" and ")
Bill and Bob and Steve

Java a-t-il quelque chose comme ça? Je sais que je peux bricoler quelque chose avec StringBuilder:

static public String join(List<String> list, String conjunction)
{
   StringBuilder sb = new StringBuilder();
   boolean first = true;
   for (String item : list)
   {
      if (first)
         first = false;
      else
         sb.append(conjunction);
      sb.append(item);
   }
   return sb.toString();
}

... mais cela ne sert à rien si quelque chose comme ça fait déjà partie du JDK.


1
Voir aussi cette question pour les listes
Casebash

18
Pas strictement lié mais Android a une fonction de jointure intégrée dans le cadre de sa classe TextUtils: developer.android.com/reference/android/text/… , java.lang.Iterable)
Xavi

16
Java 8 a une String.join()méthode. Jetez un oeil à cette réponse si vous utilisez Java 8 (ou plus récent) stackoverflow.com/a/22577565/1115554
micha

Réponses:


764

Avec Java 8, vous pouvez le faire sans aucune bibliothèque tierce.

Si vous souhaitez rejoindre une collection de chaînes, vous pouvez utiliser la nouvelle méthode String.join () :

List<String> list = Arrays.asList("foo", "bar", "baz");
String joined = String.join(" and ", list); // "foo and bar and baz"

Si vous avez une collection avec un autre type que String, vous pouvez utiliser l'API Stream avec le collecteur joint :

List<Person> list = Arrays.asList(
  new Person("John", "Smith"),
  new Person("Anna", "Martinez"),
  new Person("Paul", "Watson ")
);

String joinedFirstNames = list.stream()
  .map(Person::getFirstName)
  .collect(Collectors.joining(", ")); // "John, Anna, Paul"

La StringJoinerclasse peut également être utile.


7
Malheureusement, String.join n'accepte que les CharSequences et non comme on pourrait l'espérer. Je ne sais même pas si c'est nullsafe
Marc

@MarcassertThat(String.join(", ", Lists.newArrayList("1", null)), is("1, null"));
Sanghyun Lee

StringJoiner est particulièrement utile si l'on veut se joindre à quelque chose comme [a, b, c]inclure les accolades.
koppor

@Marc String.join accepte également Iterable<CharSequence>; Relation d'interface:Iterable -> Collection -> List
aff

306

Toutes les références à Apache Commons sont correctes (et c'est ce que la plupart des gens utilisent) mais je pense que l' équivalent de la goyave , Joiner , a une API beaucoup plus agréable.

Vous pouvez faire le cas de jointure simple avec

Joiner.on(" and ").join(names)

mais aussi gérer facilement les nulls:

Joiner.on(" and ").skipNulls().join(names);

ou

Joiner.on(" and ").useForNull("[unknown]").join(names);

et (assez utile en ce qui me concerne pour l'utiliser de préférence à commons-lang), la possibilité de gérer Maps:

Map<String, Integer> ages = .....;
String foo = Joiner.on(", ").withKeyValueSeparator(" is ").join(ages);
// Outputs:
// Bill is 25, Joe is 30, Betty is 35

ce qui est extrêmement utile pour le débogage, etc.


10
merci pour la prise! Notre Joiner vous donne également la possibilité d'ajouter directement à un Appendable (tel qu'un StringBuilder ou tout Writer) sans créer de chaînes intermédiaires, ce que la bibliothèque Apache semble manquer.
Kevin Bourrillion

2
C'est très bien, mais pouvez-vous s'il vous plaît ajouter une .useForLastSeparator()ou une méthode similaire? De cette façon, vous pourriez obtenir quelque chose comme "et" entre seulement les deux derniers éléments (avec "," pour le reste).
Jeff Evans

2
Oui, c'est thread-safe. Le seul état enregistré dans un menuisier est le separator(qui est final). Tout ce que j'ai vu à Guava, où j'utilisais un équivalent d'Apache Commons, était tellement mieux (lire: plus propre, plus rapide, plus sûr et juste plus robuste en général) à Guava avec moins de défaillances de cas de bord et de problèmes de sécurité des threads et encombrement mémoire réduit. Leur principe directeur "de la meilleure façon possible" semble être vrai jusqu'à présent.
Shadow Man

Si vous devez aller dans la direction opposée (c'est-à-dire diviser une chaîne en morceaux), consultez le séparateur de classe Guava - également très bien conçu / mis en œuvre.
Matt Passell

En Java 8, il existe une String.join()méthode et une StringJoinerclasse.
theTechnoKid

138

Pas prêt à l'emploi, mais de nombreuses bibliothèques ont des similitudes:

Commons Lang:

org.apache.commons.lang.StringUtils.join(list, conjunction);

Printemps:

org.springframework.util.StringUtils.collectionToDelimitedString(list, conjunction);

125

Sur Android, vous pouvez utiliser la classe TextUtils .

TextUtils.join(" and ", names);

11
Je cherchais ça. String.joinnécessite un niveau minimum d'api 26 sur android.
okarakose du

49

Non, il n'y a pas une telle méthode pratique dans l'API Java standard.

Sans surprise, Apache Commons fournit une telle chose dans leur classe StringUtils au cas où vous ne voudriez pas l'écrire vous-même.


11
Cela me dérange toujours que String ait un split mais pas une jointure. D'une certaine manière, cela a du sens, mais c'est juste ennuyeux qu'il n'y ait pas au moins une méthode statique pour cela dans String.
Powerlord

3
Je suis avec toi Bemrose. Au moins, ils nous ont donné une isEmpty()méthode au lieu d' une méthode (statique) join()dans String...: rollseyes: :)
Bart Kiers

41

Trois possibilités en Java 8:

List<String> list = Arrays.asList("Alice", "Bob", "Charlie")

String result = String.join(" and ", list);

result = list.stream().collect(Collectors.joining(" and "));

result = list.stream().reduce((t, u) -> t + " and " + u).orElse("");

27

Avec un collecteur java 8, cela peut être fait avec le code suivant:

Arrays.asList("Bill", "Bob", "Steve").stream()
.collect(Collectors.joining(" and "));

De plus, la solution la plus simple en java 8:

String.join(" and ", "Bill", "Bob", "Steve");

ou

String.join(" and ", Arrays.asList("Bill", "Bob", "Steve"));

21

J'ai écrit celui-ci (je l'utilise pour les beans et l'exploiter toString, alors n'écrivez pas Collection<String>):

public static String join(Collection<?> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<?> 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 TLD, 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 l'utiliser dans les fichiers JSP comme:

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

1
suggérer de changer Collection<>pour Iterable<>?
Jason S

@JasonS +1. Bon point, mais lisez stackoverflow.com/questions/1159797/…
gavenkoa

19

Le code dont vous disposez est la bonne façon de le faire si vous souhaitez utiliser JDK sans bibliothèque externe. Il n'y a pas de "one-liner" simple que vous pourriez utiliser dans JDK.

Si vous pouvez utiliser des bibliothèques externes, je vous recommande de consulter la classe org.apache.commons.lang.StringUtils dans la bibliothèque Apache Commons.

Un exemple d'utilisation:

List<String> list = Arrays.asList("Bill", "Bob", "Steve");
String joinedResult = StringUtils.join(list, " and ");

17

Un moyen orthodoxe pour y parvenir, est de définir une nouvelle fonction:

public static String join(String joinStr, String... strings) {
    if (strings == null || strings.length == 0) {
        return "";
    } else if (strings.length == 1) {
        return strings[0];
    } else {
        StringBuilder sb = new StringBuilder(strings.length * 1 + strings[0].length());
        sb.append(strings[0]);
        for (int i = 1; i < strings.length; i++) {
            sb.append(joinStr).append(strings[i]);
        }
        return sb.toString();
    }
}

Échantillon:

String[] array = new String[] { "7, 7, 7", "Bill", "Bob", "Steve",
        "[Bill]", "1,2,3", "Apple ][","~,~" };

String joined;
joined = join(" and ","7, 7, 7", "Bill", "Bob", "Steve", "[Bill]", "1,2,3", "Apple ][","~,~");
joined = join(" and ", array); // same result

System.out.println(joined);

Production:

7, 7, 7 et Bill et Bob et Steve et [Bill] et 1,2,3 et Apple] [et ~, ~


5
donner un paramètre de méthode du même nom que la méthode elle-même n'est probablement pas la meilleure idée. separatorserait beaucoup plus suggestif.
ccpizza

10

Solution Java 8 avec java.util.StringJoiner

Java 8 a une StringJoinerclasse. Mais vous devez encore écrire un peu de passe-partout, car c'est Java.

StringJoiner sj = new StringJoiner(" and ", "" , "");
String[] names = {"Bill", "Bob", "Steve"};
for (String name : names) {
   sj.add(name);
}
System.out.println(sj);

Vous n'avez pas besoin d'écrire un peu de passe-partout si vous utilisez la méthode plus pratique String.join () .
Andy Thomas

9

Vous pouvez utiliser la bibliothèque apache commons qui a une classe StringUtils et une méthode join.

Vérifiez ce lien: https://commons.apache.org/proper/commons-lang/javadocs/api.2.0/org/apache/commons/lang/StringUtils.html

Notez que le lien ci-dessus peut devenir obsolète au fil du temps, auquel cas vous pouvez simplement rechercher sur le Web "apache commons StringUtils", qui devrait vous permettre de trouver la dernière référence.

(référencé à partir de ce fil) Équivalents Java de C # String.Format () et String.Join ()


7

Tu peux le faire:

String aToString = java.util.Arrays.toString(anArray);
// Do not need to do this if you are OK with '[' and ']'
aToString = aToString.substring(1, aToString.length() - 1);

Ou une ligne (uniquement lorsque vous ne voulez pas «[» et «]»)

String aToString = java.util.Arrays.toString(anArray).substring(1).replaceAll("\\]$", "");

J'espère que cela t'aides.


1
Cela n'ajoutera pas la conjonction de choix.
hexium

OOPS!! Je suis désolé, je ne l'ai pas remarqué.
NawaMan

6

Une façon amusante de le faire avec du JDK pur, dans une seule ligne de service:

String[] array = new String[] { "Bill", "Bob", "Steve","[Bill]","1,2,3","Apple ][" };
String join = " and ";

String joined = Arrays.toString(array).replaceAll(", ", join)
        .replaceAll("(^\\[)|(\\]$)", "");

System.out.println(joined);

Production:

Bill et Bob et Steve et [Bill] et 1,2,3 et Apple] [


Une manière pas trop parfaite et pas trop amusante!

String[] array = new String[] { "7, 7, 7","Bill", "Bob", "Steve", "[Bill]",
        "1,2,3", "Apple ][" };
String join = " and ";

for (int i = 0; i < array.length; i++) array[i] = array[i].replaceAll(", ", "~,~");
String joined = Arrays.toString(array).replaceAll(", ", join)
        .replaceAll("(^\\[)|(\\]$)", "").replaceAll("~,~", ", ");

System.out.println(joined);

Production:

7, 7, 7 et Bill et Bob et Steve et [Bill] et 1,2,3 et Apple] [


Essayez-le avec "[Bill]", "1,2,3" et "Apple] [". Créatif, mais il a des cas où il est incorrect.
Jason S

1
Essayez-le maintenant avec "~, ~". Vous ne pouvez pas faire un programme avec ce type d'architecture complètement à l'épreuve des balles.
Jason S

Ouais, je sais ... j'ai supprimé le downvote. Mais vous devriez réfléchir un peu plus attentivement à la publication des réponses ici, en particulier pour les questions comme celle-ci qui datent de plusieurs années. Non seulement les réponses sont immédiatement utiles à l'affiche originale, mais elles apparaissent également dans les recherches Google. (En fait, pour les questions qui datent de plusieurs années, les nouvelles réponses peuvent ne pas avoir de valeur pour l'affiche originale.) Les solutions présentant des inconvénients ou des erreurs peuvent être nocives pour les personnes qui les trouvent plus tard hors contexte.
Jason S

1
J'apprends beaucoup ici d'autres articles comme le mien, sans solution parfaite, mais vraiment riche de connaissances et de réflexion. Détendez-vous pour les autres, chacun doit comprendre ce que fait chaque ligne de son code. N'oubliez pas que la programmation est comme un art, et même chaque ligne de code signifie quelque chose sur la personnalité du programmeur. Et oui, je n'utiliserai pas ce code en production!
Daniel De León


4

Si vous utilisez des collections Eclipse (anciennement collections GS ), vous pouvez utiliser la makeString()méthode.

List<String> list = Arrays.asList("Bill", "Bob", "Steve");

String string = ListAdapter.adapt(list).makeString(" and ");

Assert.assertEquals("Bill and Bob and Steve", string);

Si vous pouvez convertir votre Listen un type de collections Eclipse, vous pouvez vous débarrasser de l'adaptateur.

MutableList<String> list = Lists.mutable.with("Bill", "Bob", "Steve");
String string = list.makeString(" and ");

Si vous voulez juste une chaîne séparée par des virgules, vous pouvez utiliser la version makeString()qui ne prend aucun paramètre.

Assert.assertEquals(
    "Bill, Bob, Steve", 
    Lists.mutable.with("Bill", "Bob", "Steve").makeString());

Remarque: je suis un committer pour les collections Eclipse.


3

Avec java 1.8 stream peut être utilisé,

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
List<String> list = Arrays.asList("Bill","Bob","Steve").
String str = list.stream().collect(Collectors.joining(" and "));

3

ÉDITER

Je remarque également le toString()problème d'implémentation sous - jacent et l'élément contenant le séparateur, mais je pensais que j'étais paranoïaque.

Étant donné que j'ai deux commentaires à ce sujet, je change ma réponse en:

static String join( List<String> list , String replacement  ) {
    StringBuilder b = new StringBuilder();
    for( String item: list ) { 
        b.append( replacement ).append( item );
    }
    return b.toString().substring( replacement.length() );
}

Ce qui ressemble assez à la question d'origine.

Donc, si vous n'avez pas envie d'ajouter le pot entier à votre projet, vous pouvez l'utiliser.

Je pense qu'il n'y a rien de mal avec votre code d'origine. En fait, l'alternative que tout le monde suggère est presque la même (bien qu'elle fasse un certain nombre de validations supplémentaires)

Le voici, avec la licence Apache 2.0.

public static String join(Iterator iterator, String separator) {
    // handle null, zero and one elements before building a buffer
    if (iterator == null) {
        return null;
    }
    if (!iterator.hasNext()) {
        return EMPTY;
    }
    Object first = iterator.next();
    if (!iterator.hasNext()) {
        return ObjectUtils.toString(first);
    }

    // two or more elements
    StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small
    if (first != null) {
        buf.append(first);
    }

    while (iterator.hasNext()) {
        if (separator != null) {
            buf.append(separator);
        }
        Object obj = iterator.next();
        if (obj != null) {
            buf.append(obj);
        }
    }
    return buf.toString();
}

Maintenant, nous savons, merci open source


Cela fonctionnera maintenant, mais vous ne pouvez pas être sûr du comportement futur de List.toString (). Je ne ferais jamais confiance à une opération toString () autre que l'impression d'une chaîne d'information sur l'objet en question. Pour votre défense, vous n'êtes pas le seul à proposer cette solution.
Hans Doggen

Que faire si le contenu d'un élément de la liste contient la sous ", "- chaîne ?
Bart Kiers

@Hans & @Bart: Vous avez raison. Je pensais que personne d'autre ne remarquerait: P Je change ma réponse.
OscarRyz

Cela dépend du cas d'utilisation. À des fins de débogage, toString () est très bien IMO ... à condition de ne pas compter sur un formatage particulier. Et même ainsi, si vous écrivez un test pour votre code, toutes les modifications futures seront prises.
akostadinov

2

L'API Guava de Google a également .join (), bien que (comme cela devrait être évident avec les autres réponses), Apache Commons soit à peu près la norme ici.


1

Java 8 apporte le

Collectors.joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)

, c'est-à-dire nullsafe en utilisant prefix + suffixdes valeurs nulles.

Il peut être utilisé de la manière suivante:

String s = stringList.stream().collect(Collectors.joining(" and ", "prefix_", "_suffix"))

La Collectors.joining(CharSequence delimiter)méthode appelle simplement en joining(delimiter, "", "")interne.


0

Vous pouvez l'utiliser à partir de StringUtils de Spring Framework. Je sais qu'il a déjà été mentionné, mais vous pouvez réellement prendre ce code et cela fonctionne immédiatement, sans avoir besoin de Spring pour cela.

// from https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/main/java/org/springframework/util/StringUtils.java

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
public class StringUtils {
    public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) {
        if(coll == null || coll.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        Iterator<?> it = coll.iterator();
        while (it.hasNext()) {
            sb.append(prefix).append(it.next()).append(suffix);
            if (it.hasNext()) {
                sb.append(delim);
            }
        }
        return sb.toString();
    }
}

-3

Essaye ça:

java.util.Arrays.toString(anArray).replaceAll(", ", ",")
                .replaceFirst("^\\[","").replaceFirst("\\]$","");

1
Probablement parce que la question appelle à l'utilisation d'une liste par rapport à un tableau? haussement d'épaules
Nick Coelius

4
Parce que si vous avez un "," dans vos chaînes, cela ne fonctionnera pas.
Cyrille Pontvieux
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.