Java: converti Elenco <String> in una stringa


596

JavaScript ha Array.join()

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

Java ha qualcosa del genere? So che posso mettere insieme qualcosa da solo con 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();
}

... ma non ha senso farlo se qualcosa del genere fa già parte del JDK.


1
Vedi anche questa domanda per gli elenchi
Casebash,

18
Non strettamente correlato, ma Android ha una funzione di join integrata come parte della loro classe TextUtils: developer.android.com/reference/android/text/… , java.lang.Iterable)
Xavi

16
Java 8 ha un String.join()metodo. Dai un'occhiata a questa risposta se stai usando Java 8 (o più recente) stackoverflow.com/a/22577565/1115554
micha

Risposte:


764

Con Java 8 puoi farlo senza alcuna libreria di terze parti.

Se vuoi unirti a una raccolta di stringhe puoi usare il nuovo metodo String.join () :

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

Se disponi di una raccolta con un tipo diverso da String, puoi utilizzare l'API Stream con il raccoglitore partecipante :

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 può anche essere utile.


7
Sfortunatamente, String.join accetta solo CharSequences e non come si aspetterebbe Oggetti. Non sono nemmeno sicuro che sia nullsafe
Marc

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

StringJoiner è particolarmente utile se si vuole unirsi a qualcosa come l' [a, b, c]inclusione delle parentesi graffe.
koppor,

@Marc String.join accetta anche Iterable<CharSequence>; Relazione di interfaccia:Iterable -> Collection -> List
aff

306

Tutti i riferimenti ad Apache Commons vanno bene (ed è quello che la maggior parte delle persone usa) ma penso che l' equivalente di Guava , Joiner , abbia un'API molto più bella.

Puoi fare il semplice caso di join con

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

ma anche gestire facilmente i null:

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

o

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

e (abbastanza utile per quanto mi riguarda per usarlo preferibilmente alla lingua comune), la capacità di gestire Maps:

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

che è estremamente utile per il debug ecc.


10
grazie per la spina! Il nostro Joiner ti dà anche la possibilità di aggiungere direttamente un Appendable (come StringBuilder o qualsiasi Writer) senza creare stringhe intermedie, che sembra mancare alla libreria Apache.
Kevin Bourrillion,

2
Questo è fantastico, ma puoi per favore aggiungere un .useForLastSeparator()metodo simile? In questo modo, potresti ottenere qualcosa come ", e" tra solo gli ultimi due elementi (con "," per il resto).
Jeff Evans,

2
Sì, è thread-safe. L'unico stato salvato in un falegname è separator(che è final). Tutto ciò che ho visto in Guava, dove usavo un equivalente di Apache Commons, è stato molto meglio (leggi: più pulito, più veloce, più sicuro e solo più robusto in generale) in Guava con meno guasti ai bordi e problemi di sicurezza dei thread e ingombro di memoria ridotto. Il principio guida del "miglior modo possibile" sembra valere finora.
Shadow Man,

Se devi andare nella direzione opposta (cioè dividere una stringa in pezzi), dai un'occhiata allo splitter di classe Guava, anch'esso molto ben progettato / implementato.
Matt Passell,

In Java 8, c'è un String.join()metodo e una StringJoinerclasse.
theTechnoKid,

138

Non pronto all'uso, ma molte librerie hanno simili:

Commons Lang:

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

Primavera:

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

125

Su Android puoi usare la classe TextUtils .

TextUtils.join(" and ", names);

11
Lo stavo cercando. String.joinrichiede un livello minimo di API 26 su Android.
Okarakose,

49

No, non esiste tale metodo di convenienza nell'API Java standard.

Non sorprende che Apache Commons fornisca una cosa del genere nella loro classe StringUtils nel caso in cui non si desideri scriverla da soli.


11
Mi ha sempre infastidito il fatto che String abbia una divisione ma non un join. In un certo senso ha senso, ma è solo fastidioso che non ci sia almeno un metodo statico per esso in String.
Powerlord,

3
Sono con te, Bemrose. Almeno ci hanno dato un isEmpty()metodo anziché un metodo (statico) join()in String...: rollseyes: :)
Bart Kiers,

41

Tre possibilità in 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

Con un raccoglitore java 8, questo può essere fatto con il seguente codice:

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

Inoltre, la soluzione più semplice in java 8:

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

o

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

21

Ho scritto questo (lo uso per bean e exploit toString, quindi non scrivere 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();
}

ma Collectionnon è supportato da JSP, quindi per TLD ho scritto:

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();
}

e metti nel .tldfile:

<?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>

e utilizzarlo nei file JSP come:

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

1
suggerire di cambiare Collection<>in Iterable<>?
Jason S

@JasonS +1. Buon punto, ma di lettura stackoverflow.com/questions/1159797/...
gavenkoa

19

Il codice che hai è il modo giusto per farlo se vuoi usare JDK senza librerie esterne. Non esiste un semplice "one-liner" che è possibile utilizzare in JDK.

Se puoi usare le librerie esterne, ti consiglio di consultare org.apache.commons.lang.StringUtils classe nella libreria di Apache Commons.

Un esempio di utilizzo:

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

17

Un modo ortodosso per ottenerlo è quello di definire una nuova funzione:

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();
    }
}

Campione:

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);

Produzione:

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


5
assegnare a un parametro del metodo lo stesso nome del metodo stesso non è probabilmente la migliore idea. separatorsarebbe molto più suggestivo.
ccpizza,

10

Soluzione Java 8 con java.util.StringJoiner

Java 8 ha una StringJoinerclasse. Ma devi ancora scrivere un po 'di boilerplate, perché è Java.

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

Non è necessario scrivere un po 'di boilerplate se si utilizza il metodo più conveniente String.join () .
Andy Thomas,

9

Puoi usare la libreria commons apache che ha una classe StringUtils e un metodo join.

Controlla questo link: https://commons.apache.org/proper/commons-lang/javadocs/api.2.0/org/apache/commons/lang/StringUtils.html

Nota che il link sopra potrebbe diventare obsoleto nel tempo, nel qual caso puoi semplicemente cercare sul web "apache commons StringUtils", che dovrebbe permetterti di trovare il riferimento più recente.

(a cui fa riferimento questo thread) Equivalenti Java di C # String.Format () e String.Join ()


7

Puoi farlo:

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);

O un one-liner (solo quando non si desidera '[' e ']')

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

Spero che sia di aiuto.


1
Questo non aggiungerà la congiunzione di scelta.
esio,

OOPS !! Mi dispiace, non l'ho notato.
NawaMan,

6

Un modo divertente per farlo con JDK puro, in una sola riga:

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);

Produzione:

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


Un modo non troppo perfetto e non troppo divertente!

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);

Produzione:

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


Provalo con "[Bill]", "1,2,3" e "Apple] [". Creativo, ma ha casi in cui non è corretto.
Jason S,

1
Ora provalo con "~, ~". Non è possibile creare un programma con questo tipo di architettura completamente a prova di proiettile.
Jason S,

Sì, lo so ... Ho rimosso il downvote. Ma dovresti pensare un po 'più attentamente a pubblicare le risposte qui, specialmente per domande come questa che hanno diversi anni. Non solo le risposte sono immediatamente utili al poster originale, ma vengono anche visualizzate nelle ricerche di Google. (In effetti, per domande che hanno diversi anni, le nuove risposte potrebbero non avere alcun valore per il poster originale.) Le soluzioni che presentano svantaggi o errori possono essere dannose per le persone che lo trovano in seguito fuori contesto.
Jason S,

1
Qui imparo molto da altri post come il mio, con una soluzione non perfetta, ma davvero ricchi di conoscenza e pensiero secondario. Rilassati per gli altri, ognuno deve capire cosa fa ogni riga del proprio codice. Ricorda che programmare è come un'arte e anche ogni riga di codice significa qualcosa sulla personalità del programmatore. E sì, non userò questo codice in produzione!
Daniel De León,


4

Se si utilizzano le raccolte Eclipse (precedentemente raccolte GS ), è possibile utilizzare il makeString()metodo

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

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

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

Se riesci a convertire il tuo Listin un tipo di raccolte Eclipse, puoi eliminare l'adattatore.

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

Se si desidera solo una stringa separata da virgola, è possibile utilizzare la versione makeString()che non accetta parametri.

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

Nota: sono un committer per le raccolte Eclipse.


3

Con java 1.8 puoi usare stream,

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

MODIFICARE

Noto anche il toString() problema dell'implementazione sottostante e dell'elemento contenente il separatore, ma pensavo di essere paranoico.

Dal momento che ho due commenti al riguardo, sto cambiando la mia risposta a:

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() );
}

Che sembra abbastanza simile alla domanda originale.

Quindi, se non hai voglia di aggiungere l'intero vaso al tuo progetto, puoi usarlo.

Penso che non ci sia nulla di sbagliato nel tuo codice originale. In realtà, l'alternativa suggerita da tutti sembra quasi la stessa (anche se fa un numero di validazioni aggiuntive)

Eccolo qui, insieme alla licenza 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();
}

Ora sappiamo, grazie all'open source


Funzionerà ora, ma non puoi essere sicuro di come si comporterà List.toString () in futuro. Non mi fiderei mai di un'operazione toString () diversa dalla stampa di una stringa informativa sull'oggetto in questione. A tua difesa, non sei l'unico a proporre questa soluzione.
Hans Doggen,

Che cosa succede se il contenuto di un elemento nell'elenco contiene la sottostringa ", " ?
Bart Kiers,

@Hans & @Bart: hai ragione. Pensavo che nessun altro se ne accorgerebbe: P Sto cambiando la mia risposta.
OscarRyz,

Dipende dal caso d'uso. Ai fini del debug toString () va bene IMO ... a condizione che non si faccia affidamento su una formattazione particolare. E anche così, se scrivi un test per il tuo codice, verranno prese eventuali modifiche future.
Akostadinov,

2

L'API Guava di Google ha anche .join (), sebbene (come dovrebbe essere ovvio con le altre risposte), Apache Commons è praticamente lo standard qui.


1

Java 8 porta il

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

metodo, ovvero nullsafe utilizzando prefix + suffix per valori null.

Può essere utilizzato nel modo seguente:

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

Il Collectors.joining(CharSequence delimiter)metodo chiama solo joining(delimiter, "", "")internamente.


0

Puoi usarlo da StringUtils di Spring Framework. So che è già stato menzionato, ma in realtà puoi semplicemente prendere questo codice e funziona immediatamente, senza bisogno di Spring per questo.

// 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

Prova questo:

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

1
Probabilmente perché la domanda richiede l'utilizzo di un elenco rispetto a un array? scrollata di spalle
Nick Coelius,

4
Perché se hai un "," nelle tue stringhe non funzionerà.
Cyrille Pontvieux,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.