Cara termudah untuk membatasi daftar dengan koma?

104

Apa cara paling jelas untuk memisahkan koma-batasi daftar di Java?

Saya tahu beberapa cara melakukannya, tetapi saya bertanya-tanya apa cara terbaik (di mana "terbaik" berarti paling jelas dan / atau terpendek, bukan yang paling efisien.

Saya memiliki daftar dan saya ingin mengulanginya, mencetak setiap nilai. Saya ingin mencetak koma di antara setiap item, tetapi tidak setelah yang terakhir (atau sebelum yang pertama).

List --> Item ( , Item ) *
List --> ( Item , ) * Item

Solusi sampel 1:

boolean isFirst = true;
for (Item i : list) {
  if (isFirst) {
    System.out.print(i);        // no comma
    isFirst = false;
  } else {
    System.out.print(", "+i);   // comma
  }
}

Solusi sampel 2 - buat sublist:

if (list.size()>0) {
  System.out.print(list.get(0));   // no comma
  List theRest = list.subList(1, list.size());
  for (Item i : theRest) {
    System.out.print(", "+i);   // comma
  }
}

Solusi sampel 3:

  Iterator<Item> i = list.iterator();
  if (i.hasNext()) {
    System.out.print(i.next());
    while (i.hasNext())
      System.out.print(", "+i.next());
  }

Ini memperlakukan item pertama secara khusus; seseorang malah bisa memperlakukan yang terakhir secara khusus.

Kebetulan, di sini adalah bagaimana List toString yang dilaksanakan (itu diwarisi dari AbstractCollection), di Java 1.6:

public String toString() {
    Iterator<E> i = iterator();
    if (! i.hasNext())
        return "[]";

    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {
        E e = i.next();
        sb.append(e == this ? "(this Collection)" : e);
        if (! i.hasNext())
            return sb.append(']').toString();
        sb.append(", ");
    }
}

Ini keluar dari loop lebih awal untuk menghindari koma setelah item terakhir. BTW: ini pertama kalinya saya ingat melihat "(Koleksi ini)"; berikut kode untuk memprovokasi itu:

List l = new LinkedList();
l.add(l);
System.out.println(l);

Saya menyambut baik solusi apapun, bahkan jika mereka menggunakan perpustakaan yang tidak terduga (regexp?); dan juga solusi dalam bahasa lain selain Java (misalnya saya pikir Python / Ruby memiliki menyelingi fungsi - bagaimana itu ? diimplementasikan).

Klarifikasi : menurut perpustakaan, yang saya maksud adalah perpustakaan Java standar. Untuk perpustakaan lain, saya mempertimbangkannya dengan bahasa lain, dan tertarik untuk mengetahui bagaimana penerapannya.

Toolkit EDIT menyebutkan pertanyaan serupa: iterasi terakhir untuk loop ditingkatkan di java

Dan lainnya: Apakah elemen terakhir dalam sebuah loop membutuhkan perlakuan terpisah?

13ren
sumber

Jawaban:

226

Java 8 dan yang lebih baru

Menggunakan StringJoinerkelas:

StringJoiner joiner = new StringJoiner(",");
for (Item item : list) {
    joiner.add(item.toString());
}
return joiner.toString();

Menggunakan Stream, dan Collectors:

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

Java 7 dan sebelumnya

Lihat juga # 285523

String delim = "";
for (Item i : list) {
    sb.append(delim).append(i);
    delim = ",";
}
toolkit
sumber
1
Cara yang sangat mengejutkan untuk menyimpan status! Ini hampir polimorfik OO. Saya tidak suka tugas yang tidak perlu setiap loop, tapi saya yakin itu lebih efisien daripada if. Sebagian besar solusi mengulangi pengujian yang kami tahu tidak akan benar - meskipun tidak efisien, solusi tersebut mungkin paling jelas. Terima kasih untuk tautannya!
13ren
1
Saya suka ini, tetapi karena ini pintar dan mengejutkan, saya pikir itu tidak akan sesuai untuk digunakan (mengejutkan tidak baik!). Namun, saya tidak bisa menahan diri. Saya masih tidak yakin tentang itu; namun, ini sangat singkat sehingga mudah dikerjakan asalkan ada komentar untuk memberi petunjuk.
13ren
6
@ 13ren: Anda dan Paman Bob perlu berbicara tentang kode yang "pintar dan mengejutkan."
Stefan Kendall
Saya merasa seperti menghabiskan waktu bertahun-tahun tanpa mengetahui hal ini ^^;
Danau
Mengapa Java perlu mengubah tugas seperti itu menjadi kode yang jelek?
Eduardo
82
org.apache.commons.lang3.StringUtils.join(list,",");
trunkc
sumber
1
Menemukan sumbernya: svn.apache.org/viewvc/commons/proper/lang/trunk/src/java/org/… Metode bergabung memiliki 9 beban berlebih. Yang berbasis iterasi seperti "Solusi sampel 3"; yang berbasis indeks menggunakan: if (i> startIndex) {<add separator>}
13ren
Saya benar-benar setelah implementasi. Ide bagus untuk membungkusnya dalam sebuah fungsi, tetapi dalam praktiknya, pembatas saya terkadang berupa baris baru, dan setiap baris juga menjorok ke kedalaman tertentu. Kecuali ... gabung (daftar, "\ n" + indentasi) ... akankah itu selalu berhasil? Maaf, hanya berpikir keras.
13ren
2
Menurut pendapat saya, ini adalah solusi tercantik dan terpendek
Jesper Rønn-Jensen
mungkin, list.iterator ()?
Vanuan
32

Java 8 menyediakan beberapa cara baru untuk melakukan ini:

Contoh:

// Util method for strings and other char sequences
List<String> strs = Arrays.asList("1", "2", "3");
String listStr1 = String.join(",", strs);

// For any type using streams and collectors
List<Object> objs = Arrays.asList(1, 2, 3);
String listStr2 = objs.stream()
    .map(Object::toString)
    .collect(joining(",", "[", "]"));

// Using the new StringJoiner class
StringJoiner joiner = new StringJoiner(", ", "[", "]");
joiner.setEmptyValue("<empty>");
for (Integer i : objs) {
  joiner.add(i.toString());
}
String listStr3 = joiner.toString();

Pendekatan menggunakan aliran mengasumsikan import static java.util.stream.Collectors.joining;.

Lii
sumber
Ini IMHO, jika Anda menggunakan java 8, jawaban terbaik.
gesekan
18
Joiner.on(",").join(myList)

Joiner .

Stefan Kendall
sumber
Saya datang ke sini mencari sesuatu yang melakukan hal ini. Terima kasih! +1
javamonkey79
2
Sama. Terima kasih untuk ini. Akhirnya, seseorang menjadi waras.
TWR Cole
7

Jika Anda menggunakan Spring Framework, Anda dapat melakukannya dengan StringUtils :

public static String arrayToDelimitedString(Object[] arr)
public static String arrayToDelimitedString(Object[] arr, String delim)
public static String collectionToCommaDelimitedString(Collection coll)
public static String collectionToCommaDelimitedString(Collection coll, String delim)
Henryk Konsek
sumber
Terima kasih, tapi bagaimana penerapannya?
13ren
6

Berdasarkan implementasi List toString Java:

Iterator i = list.iterator();
for (;;) {
  sb.append(i.next());
  if (! i.hasNext()) break;
  ab.append(", ");
}

Ini menggunakan tata bahasa seperti ini:

List --> (Item , )* Item

Dengan menjadi berbasis terakhir dan bukan berbasis pertama, itu dapat memeriksa lewati-koma dengan tes yang sama untuk memeriksa akhir daftar. Saya pikir yang ini sangat elegan, tapi saya tidak yakin tentang kejelasannya.

13ren
sumber
4
String delimiter = ",";
StringBuilder sb = new StringBuilder();
for (Item i : list) {
    sb.append(delimiter).append(i);
}
sb.toString().replaceFirst(delimiter, "");
Alfredo Osorio
sumber
4

Ada cara yang bagus untuk mencapai ini menggunakan Java 8:

List<String> list = Arrays.asList(array);
String joinedString = String.join(",", list);
Krishnanunni PV
sumber
TextUtils.join (",", list);
Arul Pandian
3
for(int i=0, length=list.size(); i<length; i++)
  result+=(i==0?"":", ") + list.get(i);
cherouvim
sumber
Ya, ini agak samar, tetapi alternatif dari apa yang sudah diposting.
cherouvim
1
Saya pikir kodenya terlihat bagus, IMHO lebih cantik dari jawaban Anda yang lain dan hampir 'samar'. Akhiri dengan metode yang disebut Gabung dan tawa Anda, saya tetap berharap bahasa ini memiliki cara yang lebih baik untuk memecahkan masalah yang sebenarnya umum.
tarn
@tarn: menurut Anda ini terlihat bagus karena Anda memiliki latar belakang Python / perl;) Saya juga menyukainya
cherouvim
3

Salah satu opsi untuk foreach loop adalah:

StringBuilder sb = new StringBuilder();
for(String s:list){
  if (sb.length()>0) sb.append(",");
  sb.append(s);
}
BAH
sumber
2
StringBuffer result = new StringBuffer();
for(Iterator it=list.iterator; it.hasNext(); ) {
  if (result.length()>0)
    result.append(", ");
  result.append(it.next());
}

Pembaruan : Seperti yang disebutkan Dave Webb dalam komentar, ini mungkin tidak memberikan hasil yang benar jika item pertama dalam daftar adalah string kosong.

cherouvim
sumber
meh .. masih memiliki if dalam loop.
sfossen
Anda dapat menggunakan foreach loop untuk membuatnya lebih pendek.
Peter Lawrey
Itu tidak akan berfungsi jika item pertama dalam daftar adalah string kosong.
Dave Webb
@ Dave Webb: Ya, terima kasih. Pertanyaannya tidak memiliki persyaratan seperti itu.
cherouvim
@cherovium: Pertanyaannya tidak mengatakan tidak ada item yang akan menjadi string kosong jadi ini merupakan persyaratan implisit. Jika Anda membuat file CSV, bidang kosong bisa jadi sangat umum.
Dave Webb
2

Saya biasanya melakukan ini:

StringBuffer sb = new StringBuffer();
Iterator it = myList.iterator();
if (it.hasNext()) { sb.append(it.next().toString()); }
while (it.hasNext()) { sb.append(",").append(it.next().toString()); }

Meskipun saya pikir saya akan memeriksa ini mulai sekarang sesuai dengan implementasi Java;)

Alex Marshall
sumber
Itu logika yang sama dengan Contoh 3, saya suka karena tidak ada yang tidak perlu. Namun, saya tidak tahu apakah itu yang paling jelas. BTW: ini sedikit lebih efisien jika Anda meletakkan while di dalam if, dengan menghindari dua tes saat daftar kosong. Itu juga membuatnya sedikit lebih jelas, bagi saya.
13ren
Demi Tuhan, gunakan StringBuilder
scravy
2

Jika Anda dapat menggunakan Groovy (yang berjalan di JVM):

def list = ['a', 'b', 'c', 'd']
println list.join(',')
kungfoo
sumber
terima kasih, tapi saya tertarik dengan penerapannya. Bagaimana yang satu ini diterapkan?
13ren
2

(Salin tempel jawaban saya sendiri dari sini .) Banyak solusi yang dijelaskan di sini agak berlebihan, IMHO, terutama yang mengandalkan pustaka eksternal. Ada ungkapan bagus yang bersih dan jelas untuk mencapai daftar yang dipisahkan koma yang selalu saya gunakan. Ini bergantung pada operator kondisional (?):

Sunting : Solusi asli benar, tetapi tidak optimal menurut komentar. Mencoba untuk kedua kalinya:

int[] array = {1, 2, 3};
StringBuilder builder = new StringBuilder();
for (int i = 0 ;  i < array.length; i++)
       builder.append(i == 0 ? "" : ",").append(array[i]);

Ini dia, dalam 4 baris kode termasuk deklarasi array dan StringBuilder.

Edit kedua : Jika Anda berurusan dengan Iterator:

    List<Integer> list = Arrays.asList(1, 2, 3);
    StringBuilder builder = new StringBuilder();
    for (Iterator it = list.iterator(); it.hasNext();)
        builder.append(it.next()).append(it.hasNext() ? "," : "");
Julien Chastang
sumber
Ini dan stackoverflow.com/questions/668952/… di atas serupa. Singkat dan jelas, tetapi jika Anda hanya memiliki iterator, Anda harus mengonversi terlebih dahulu. Tidak efisien, tetapi mungkin sepadan dengan kejelasan pendekatan ini?
13ren
Ugh, penggabungan string membuat StringBuilder sementara dalam penggunaan StringBuilder. Tidak efisien. Lebih baik (lebih menyukai Java, tetapi kode yang berjalan lebih baik) adalah "if (i> 0) {builder.append (',');}" diikuti oleh builder.append (array [i]);
Eddie
Ya, jika Anda melihat di bytecode, Anda benar. Saya tidak percaya pertanyaan sederhana seperti itu bisa menjadi begitu rumit. Saya ingin tahu apakah kompiler dapat mengoptimalkan di sini.
Julien Chastang
Diberikan 2nd, semoga solusi yang lebih baik.
Julien Chastang
Mungkin lebih baik untuk memisahkan mereka (sehingga orang dapat memberikan suara pada satu atau lainnya).
13ren
1

Saya biasanya menggunakan sesuatu yang mirip dengan versi 3. Ini bekerja dengan baik c / c ++ / bash / ...: P

sfossen
sumber
1

Saya tidak mengkompilasinya ... tetapi harus bekerja (atau hampir bekerja).

public static <T> String toString(final List<T> list, 
                                  final String delim)
{
    final StringBuilder builder;

    builder = new StringBuilder();

    for(final T item : list)
    {
        builder.append(item);
        builder.append(delim);
    }

    // kill the last delim
    builder.setLength(builder.length() - delim.length());

    return (builder.toString());
}
TofuBeer
sumber
Saya suka cara ini, saya lebih suka format lain. Tetapi untuk menyebutkan satu perbaikan kecil: Jika daftarnya kosong, ini akan memunculkan pengecualian IndexOutOfBounds. Jadi, lakukan pemeriksaan sebelum setLength, atau gunakan Math.max (0, ...)
tb-
1

Ini sangat singkat, sangat jelas, tetapi membuat perasaan saya kengerian yang merayap. Ini juga agak canggung untuk beradaptasi dengan pembatas yang berbeda, terutama jika sebuah String (bukan char).

for (Item i : list)
  sb.append(',').append(i);
if (sb.charAt(0)==',') sb.deleteCharAt(0);

Terinspirasi oleh: Iterasi terakhir for loop di java

13ren
sumber
1
StringBuffer sb = new StringBuffer();

for (int i = 0; i < myList.size(); i++)
{ 
    if (i > 0) 
    {
        sb.append(", ");
    }

    sb.append(myList.get(i)); 
}
Aries McRae
sumber
Saya menemukan yang satu ini sangat mudah untuk dimasukkan ke dalam kelas utilitas statis, sementara juga sangat mudah dibaca dan dipahami. Itu juga tidak memerlukan variabel atau status tambahan selain daftar itu sendiri, loop dan pembatas.
Joachim H. Skeie
Ini akan memberikan sesuatu seperti:Item1Item2, Item3, Item4,
Demi Tuhan, gunakan StringBuilder
scravy
1

Saya agak menyukai pendekatan ini, yang saya temukan di blog beberapa waktu lalu. Sayangnya saya tidak ingat nama / URL blog tersebut.

Anda dapat membuat kelas utilitas / pembantu yang terlihat seperti ini:

private class Delimiter
{
    private final String delimiter;
    private boolean first = true;

    public Delimiter(String delimiter)
    {
        this.delimiter = delimiter;
    }

    @Override
    public String toString()
    {
        if (first) {
            first = false;
            return "";
        }

        return delimiter;
    }
}

Menggunakan kelas helper sesederhana ini:

StringBuilder sb = new StringBuilder();
Delimiter delimiter = new Delimiter(", ");

for (String item : list) {
    sb.append(delimiter);
    sb.append(item);
}
Tobias Müller
sumber
Sekadar catatan: itu harus "pembatas", bukan "pembatas".
Michael Myers
1

Karena pembatas Anda adalah "," Anda dapat menggunakan salah satu dari berikut ini:

public class StringDelim {

    public static void removeBrackets(String string) {
        System.out.println(string.substring(1, string.length() - 1));
    }

    public static void main(String... args) {
        // Array.toString() example
        String [] arr = {"Hi" , "My", "name", "is", "br3nt"};
        String string = Arrays.toString(arr);
        removeBrackets(string);

        // List#toString() example
        List<String> list = new ArrayList<String>();
        list.add("Hi");
        list.add("My");
        list.add("name");
        list.add("is");
        list.add("br3nt");
        string = list.toString();
        removeBrackets(string);

        // Map#values().toString() example
        Map<String, String> map = new LinkedHashMap<String, String>();
        map.put("1", "Hi");
        map.put("2", "My");
        map.put("3", "name");
        map.put("4", "is");
        map.put("5", "br3nt");
        System.out.println(map.values().toString());
        removeBrackets(string);

        // Enum#toString() example
        EnumSet<Days> set = EnumSet.allOf(Days.class);
        string = set.toString();
        removeBrackets(string);
    }

    public enum Days {
        MON("Monday"),
        TUE("Tuesday"),
        WED("Wednesday"),
        THU("Thursday"),
        FRI("Friday"),
        SAT("Saturday"),
        SUN("Sunday");

        private final String day;

        Days(String day) {this.day = day;}
        public String toString() {return this.day;}
    }
}

Jika pembatas Anda ADA, maka ini tidak akan berhasil untuk Anda.

br3nt
sumber
1

Saya suka solusi ini:

String delim = " - ", string = "";

for (String item : myCollection)
    string += delim + item;

string = string.substring(delim.length());

Saya berasumsi itu dapat menggunakan StringBuilder juga.

br3nt
sumber
1

Menurut saya, ini yang paling sederhana untuk dibaca dan dipahami:

StringBuilder sb = new StringBuilder();
for(String string : strings) {
    sb.append(string).append(',');
}
sb.setLength(sb.length() - 1);
String result = sb.toString();
SergeyB
sumber
0

Dengan Python itu mudah

",". gabung (daftar Anda)

Di C # ada metode statis pada kelas String

String.Join (",", yourlistofstrings)

Maaf, tidak yakin tentang Java tapi saya pikir saya akan melanjutkannya saat Anda bertanya tentang bahasa lain. Saya yakin akan ada yang serupa di Jawa.

tarn
sumber
IIRC, Anda mendapatkan jejak, dalam versi .Net. :-(
1
Baru saja diuji, tidak ada pembatas tambahan di .NET atau Python
tarn
1
Terima kasih! Saya menemukan sumbernya: svn.python.org/view/python/branches/release22-branch/Objects/… cari "Catenate everything". Ini menghilangkan pemisah untuk item terakhir.
13ren
@ 13ren Bagus sekali! Apakah itu membantu? Saya memiliki kunci dan dengan cepat teringat mengapa saya memilih untuk tidak memprogram dalam C hari ini: P
tarn
@tarn, ya, ini contoh yang menarik karena hanya menambahkan comman jika bukan item terakhir: if (i <seqlen - 1) {<add separator>}
13ren
0

Anda juga dapat menambahkan pembatas tanpa syarat, dan setelah pengulangan menghapus pembatas ekstra di akhir. Kemudian "jika daftar kosong maka kembalikan string ini" di awal akan memungkinkan Anda untuk menghindari pemeriksaan di akhir (karena Anda tidak dapat menghapus karakter dari daftar kosong)

Jadi pertanyaannya sebenarnya adalah:

"Diberikan lingkaran dan jika, menurut Anda apa cara paling jelas untuk menggabungkan ini?"

Thorbjørn Ravn Andersen
sumber
0
public static String join (List<String> list, String separator) {
  String listToString = "";

  if (list == null || list.isEmpty()) {
   return listToString;
  }

  for (String element : list) {
   listToString += element + separator;
  }

  listToString = listToString.substring(0, separator.length());

  return listToString;
}
pengguna441737
sumber
1
Saya pikir Anda bermaksud mengatakanlistToString.substring(0, listToString.length()-separator.length());
13ren
0
if (array.length>0)          // edited in response Joachim's comment
  sb.append(array[i]);
for (int i=1; i<array.length; i++)
  sb.append(",").append(array[i]);

Berdasarkan cara paling jelas untuk memisahkan koma daftar (Java)?

Menggunakan ide ini: Apakah elemen terakhir dalam sebuah loop layak mendapatkan perlakuan terpisah?

13ren
sumber
1
Agar ini berfungsi, Anda perlu mengetahui bahwa setidaknya ada 1 elemen dalam daftar, jika tidak, perulangan pertama Anda akan macet dengan IndexOutOfBoundsException.
Joachim H. Skeie
@Joachim terima kasih, Anda benar tentu saja. Betapa solusi jelek yang saya tulis! Saya akan berubah for (int i=0; i<1; i++)menjadi if (array.length>0).
13ren
0
public String toString(List<Item> items)
{
    StringBuilder sb = new StringBuilder("[");

    for (Item item : items)
    {
        sb.append(item).append(", ");
    }

    if (sb.length() >= 2)
    {
        //looks cleaner in C# sb.Length -= 2;
        sb.setLength(sb.length() - 2);
    }

    sb.append("]");

    return sb.toString();
}
Eblis
sumber
1
Hanya memperhatikan itu mirip dengan jawaban yang diberikan TofuBeer
Eblis
0

Sejauh ini tidak ada jawaban yang menggunakan rekursi ...

public class Main {

    public static String toString(List<String> list, char d) {
        int n = list.size();
        if(n==0) return "";
        return n > 1 ? Main.toString(list.subList(0, n - 1), d) + d
                  + list.get(n - 1) : list.get(0);
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList(new String[]{"1","2","3"});
        System.out.println(Main.toString(list, ','));
    }

}
langsweirdt
sumber
0
private String betweenComma(ArrayList<String> strings) {
    String united = "";
    for (String string : strings) {
        united = united + "," + string;
    }
    return united.replaceFirst(",", "");
}
Samet ÖZTOPRAK
sumber
-1

metode

String join(List<Object> collection, String delimiter){
    StringBuilder stringBuilder = new StringBuilder();
    int size = collection.size();
    for (Object value : collection) {
        size --;
        if(size > 0){
            stringBuilder.append(value).append(delimiter);
        }
    }

    return stringBuilder.toString();
}

Pemakaian

Diberikan larik [1,2,3]

join(myArray, ",") // 1,2,3
Ilya Gazman
sumber