Java: convert List <String> ke String


595

JavaScript memiliki Array.join()

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

Apakah Java punya yang seperti ini? Saya tahu saya dapat memperbaiki sesuatu sendiri dengan 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();
}

... tapi tidak ada gunanya melakukan ini jika sesuatu seperti itu sudah menjadi bagian dari JDK.


1
Lihat juga pertanyaan ini untuk daftar
Casebash

18
Tidak sepenuhnya terkait tetapi android memiliki fungsi gabung bawaan sebagai bagian dari kelas TextUtils mereka: developer.android.com/reference/android/text/… , java.lang.Iterable)
Xavi

16
Java 8 memiliki String.join()metode. Lihatlah jawaban ini jika Anda menggunakan Java 8 (atau lebih baru) stackoverflow.com/a/22577565/1115554
micha

Jawaban:


763

Dengan Java 8 Anda dapat melakukan ini tanpa perpustakaan pihak ketiga.

Jika Anda ingin bergabung dengan Kumpulan String, Anda dapat menggunakan metode String.join () baru :

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

Jika Anda memiliki Koleksi dengan tipe selain String, Anda dapat menggunakan Stream API dengan Collector yang bergabung :

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"

The StringJoinerkelas juga mungkin berguna.


7
Sayangnya, String.join hanya menerima CharSequences dan tidak seperti yang diharapkan Objects. Bahkan tidak yakin apakah itu nullsafe
Marc

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

StringJoiner sangat berguna jika seseorang ingin bergabung dengan sesuatu seperti [a, b, c]termasuk kawat gigi.
koppor

@Marc String.join juga menerima Iterable<CharSequence>; Hubungan antarmuka:Iterable -> Collection -> List
aff

306

Semua referensi ke Apache Commons baik-baik saja (dan itulah yang kebanyakan orang gunakan) tapi saya pikir setara Guava , Joiner , memiliki API yang jauh lebih baik.

Anda dapat melakukan kasing sederhana dengan

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

tetapi juga dengan mudah menangani nulls:

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

atau

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

dan (cukup berguna sejauh yang saya kehendaki untuk menggunakannya dalam preferensi untuk commons-lang), kemampuan untuk berurusan dengan Maps:

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

yang sangat berguna untuk debugging dll.


10
terima kasih untuk colokannya! Joiner kami juga memberi Anda opsi untuk menambahkan langsung ke Appendable (seperti StringBuilder, atau Writer mana pun) tanpa membuat String menengah, yang tampaknya kurang dimiliki oleh perpustakaan Apache.
Kevin Bourrillion

2
Ini bagus, tetapi bisakah Anda menambahkan .useForLastSeparator()metode serupa? Dengan begitu, Anda bisa mendapatkan sesuatu seperti ", dan" hanya di antara dua item terakhir (dengan "," untuk sisanya).
Jeff Evans

2
Ya, aman untuk thread. Satu-satunya negara yang disimpan dalam sebuah joiner adalah separator(yang mana final). Semua yang saya lihat di Guava, di mana saya biasa menggunakan setara Apache Commons telah jauh lebih baik (baca: lebih bersih, lebih cepat, lebih aman dan lebih kuat secara umum) di Guava dengan lebih sedikit kasus kegagalan tepi dan masalah keamanan utas dan jejak memori yang lebih kecil. Prinsip panduan "cara terbaik" mereka tampaknya benar sejauh ini.
Shadow Man

Jika Anda perlu pergi ke arah yang berlawanan (yaitu, membelah sebuah String menjadi potongan-potongan), periksa kelas Guava Splitter - juga dirancang / diimplementasikan dengan sangat baik.
Matt Passell

Di Java 8, ada String.join()metode dan StringJoinerkelas.
theTechnoKid

138

Bukan di luar kotak, tetapi banyak perpustakaan memiliki kesamaan:

Commons Lang:

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

Musim semi:

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

125

Di Android Anda bisa menggunakan kelas TextUtils .

TextUtils.join(" and ", names);

11
Saya mencari ini. String.joinmembutuhkan min api level 26 di android.
okarakose

49

Tidak, tidak ada metode kenyamanan seperti itu di API Java standar.

Tidak mengherankan, Apache Commons menyediakan hal semacam itu di kelas StringUtils mereka jika Anda tidak ingin menulisnya sendiri.


11
Selalu menyadap saya bahwa String memiliki split tetapi tidak bergabung. Dalam beberapa hal, memang masuk akal, tetapi hanya menyebalkan bahwa setidaknya tidak ada metode statis di String.
Powerlord

3
Aku bersamamu Bemrose. Setidaknya mereka memberi kami isEmpty()metode alih-alih metode (statis) join()di String...: rollseyes: :)
Bart Kiers

41

Tiga kemungkinan di Jawa 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

Dengan kolektor java 8, ini dapat dilakukan dengan kode berikut:

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

Juga, solusi paling sederhana di java 8:

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

atau

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

21

Saya menulis ini (saya menggunakannya untuk kacang dan mengeksploitasi toString, jadi jangan menulis 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();
}

tetapi Collectiontidak didukung oleh JSP, jadi untuk TLD saya menulis:

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

dan dimasukkan ke .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>

dan menggunakannya dalam file JSP sebagai:

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

1
sarankan berubah Collection<>menjadi Iterable<>?
Jason S

@JasonS +1. Poin bagus, tapi baca stackoverflow.com/questions/1159797/…
gavenkoa

19

Kode yang Anda miliki adalah cara yang benar untuk melakukannya jika Anda ingin menggunakan JDK tanpa pustaka eksternal. Tidak ada "satu garis" sederhana yang dapat Anda gunakan di JDK.

Jika Anda dapat menggunakan libs eksternal, saya sarankan Anda melihat ke kelas org.apache.commons.lang.StringUtils di pustaka Apache Commons.

Contoh penggunaan:

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

17

Cara ortodoks untuk mencapainya, adalah dengan mendefinisikan fungsi baru:

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

Sampel:

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

Keluaran:

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


5
memberikan parameter metode nama yang sama dengan metode itu sendiri mungkin bukan ide terbaik. separatorakan jauh lebih sugestif.
ccpizza

10

Solusi Java 8 dengan java.util.StringJoiner

Java 8 telah mendapat StringJoinerkelas. Tetapi Anda masih perlu menulis sedikit boilerplate, karena itu Java.

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

Anda tidak perlu menulis sedikit boilerplate jika Anda menggunakan metode String.join () yang lebih nyaman .
Andy Thomas

9

Anda bisa menggunakan pustaka apache commons yang memiliki kelas StringUtils dan metode bergabung.

Periksa tautan ini: https://commons.apache.org/proper/commons-lang/javadocs/api.2.0/org/apache/commons/lang/StringUtils.html

Perhatikan bahwa tautan di atas dapat menjadi usang seiring waktu, dalam hal ini Anda bisa mencari "apache commons StringUtils" di web, yang memungkinkan Anda menemukan referensi terbaru.

(dirujuk dari utas ini) Java equivalents dari C # String.Format () dan String.Join ()


7

Kamu bisa melakukan ini:

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

Atau satu-liner (hanya ketika Anda tidak ingin '[' dan ']')

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

Semoga ini membantu.


1
Ini tidak akan menambahkan konjungsi pilihan.
hexium

OOPS !! Maaf, saya tidak melihatnya.
NawaMan

6

Cara yang menyenangkan untuk melakukannya dengan JDK murni, dalam satu tugas:

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

Keluaran:

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


Cara yang tidak terlalu sempurna & tidak terlalu menyenangkan!

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

Keluaran:

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


Cobalah dengan "[Tagihan]", "1,2,3", dan "Apple] [". Kreatif, tetapi memiliki case ketika itu salah.
Jason S

1
Sekarang coba dengan "~, ~". Anda tidak dapat membuat program dengan arsitektur seperti ini sepenuhnya anti peluru.
Jason S

Ya, saya tahu ... Saya telah menghapus downvote. Tetapi Anda harus berpikir sedikit lebih hati-hati tentang mengirim jawaban di sini, terutama untuk pertanyaan seperti ini yang sudah berusia beberapa tahun. Tidak hanya jawaban yang langsung berguna untuk poster asli, tetapi juga muncul di pencarian Google. (Faktanya, untuk pertanyaan yang berumur beberapa tahun, jawaban baru mungkin tidak ada nilainya dengan poster aslinya.) Solusi yang memiliki kekurangan atau kesalahan dapat berbahaya bagi orang yang menemukannya kemudian di luar konteks.
Jason S

1
Saya belajar banyak di sini dari posting lain seperti milik saya, dengan solusi yang tidak sempurna, tetapi sangat kaya pengetahuan, dan pemikiran sampingan. Tenang untuk orang lain, masing-masing harus memahami apa yang dilakukan setiap baris kode mereka. Ingat pemrograman adalah seperti seni, dan bahkan setiap baris kode, berarti sesuatu tentang kepribadian programmer. Dan ya, saya tidak akan menggunakan kode ini dalam produksi!
Daniel De León


4

Jika Anda menggunakan Eclipse Collections (sebelumnya GS Collections ), Anda dapat menggunakan makeString()metode ini.

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

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

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

Jika Anda dapat mengonversi Anda Listke jenis Koleksi Eclipse, maka Anda dapat menyingkirkan adaptor.

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

Jika Anda hanya ingin string yang dipisahkan koma, Anda dapat menggunakan versi makeString()yang tidak menggunakan parameter.

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

Catatan: Saya pengendara untuk Eclipse Collections.


3

Dengan java 1.8 stream dapat digunakan,

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

EDIT

Saya juga memperhatikan toString()masalah implementasi yang mendasarinya, dan tentang elemen yang mengandung pemisah tetapi saya pikir saya sedang paranoid.

Karena saya punya dua komentar tentang hal itu, saya mengubah jawaban saya ke:

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

Yang terlihat sangat mirip dengan pertanyaan aslinya.

Jadi, jika Anda tidak ingin menambahkan seluruh toples ke proyek Anda, Anda dapat menggunakan ini.

Saya pikir tidak ada yang salah dengan kode asli Anda. Sebenarnya, alternatif yang disarankan semua orang terlihat hampir sama (meskipun ia melakukan sejumlah validasi tambahan)

Ini dia, bersama dengan lisensi 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();
}

Sekarang kami tahu, terima kasih open source


Ini akan berfungsi sekarang, tetapi Anda tidak bisa memastikan bagaimana perilaku List.toString () di masa depan. Saya tidak akan pernah mempercayai operasi toString () selain mencetak beberapa string informasi tentang objek yang dimaksud. Dalam pembelaan Anda, Anda bukan satu-satunya yang mengusulkan solusi ini.
Hans Doggen

Bagaimana jika konten elemen dalam daftar berisi substring ", "?
Bart Kiers

@Hans & @Bart: Anda benar. Saya pikir tidak ada orang lain yang akan memperhatikan: P Saya mengubah jawaban saya.
OscarRyz

Itu tergantung pada use case. Untuk keperluan debugging toString () adalah IMO yang baik ... asalkan Anda tidak bergantung pada pemformatan tertentu. Meski begitu, jika Anda menulis tes untuk kode Anda, setiap perubahan di masa depan akan ditangkap.
akostadinov

2

Google Guava API juga memiliki .join (), walaupun (seperti yang seharusnya jelas dengan balasan lainnya), Apache Commons cukup standar di sini.


1

Java 8 memang membawa

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

metode, yaitu nullsafe dengan menggunakan prefix + suffixnilai null.

Itu dapat digunakan dengan cara berikut:

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

The Collectors.joining(CharSequence delimiter)Metode hanya panggilan joining(delimiter, "", "")internal.


0

Anda dapat menggunakan ini dari StringUtils Spring Framework. Saya tahu ini sudah disebutkan, tetapi Anda benar-benar bisa mengambil kode ini dan langsung berfungsi, tanpa perlu Spring untuk itu.

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

Coba ini:

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

1
Mungkin karena pertanyaan itu menuntut penggunaan Daftar versus Array? mengangkat bahu
Nick Coelius

4
Karena jika Anda memiliki "," di string Anda, itu tidak akan berfungsi.
Cyrille Pontvieux
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.