Bir listeyi virgülle ayırmanın en basit yolu?


104

Java'da bir listeyi virgülle sınırlandırmanın en net yolu nedir?

Bunu yapmanın birkaç yolunu biliyorum, ancak en iyi yolun ne olduğunu merak ediyorum (burada "en iyi", en verimli değil, en net ve / veya en kısa anlamına gelir.

Bir listem var ve her bir değeri yazdırarak üzerinde döngü yapmak istiyorum. Her öğe arasına virgül yazdırmak istiyorum, ancak sonuncudan sonra değil (veya ilkinden önce).

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

Örnek çözüm 1:

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

Örnek çözüm 2 - bir alt liste oluşturun:

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

Örnek çözüm 3:

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

Bunlar ilk öğeyi özel olarak ele alır; bunun yerine sonuncusuna özel olarak davranılabilir.

Bu arada, burada nasıl List toString olduğu (o devralınan uygulanan AbstractCollectionJava 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(", ");
    }
}

Son öğeden sonra virgülden kaçınmak için döngüden erken çıkar. BTW: "(bu Koleksiyon)" u gördüğümü ilk kez hatırlıyorum; işte onu kışkırtacak kod:

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

Beklenmedik kitaplıklar kullansalar bile (regexp?) Herhangi bir çözümü memnuniyetle karşılıyorum; ve Java dışındaki dillerde de çözümler (örneğin Python / Ruby bir var serpmek fonksiyonu - nasıl olduğunu ? uygulanmaktadır).

Açıklama : kütüphaneler derken standart Java kütüphanelerini kastediyorum. Diğer kütüphaneler için, onları diğer dillerle birlikte değerlendiriyorum ve nasıl uygulandıklarını öğrenmekle ilgileniyorum.

DÜZENLEME araç seti benzer bir sorudan bahsetti: Java'da geliştirilmiş for döngüsünün son yinelemesi

Ve bir diğeri: Bir döngüdeki son öğe ayrı bir muameleyi hak ediyor mu?


Yanıtlar:


226

Java 8 ve üstü

StringJoinerSınıfı kullanma :

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

Kullanılması Streamve Collectors:

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

Java 7 ve öncesi

Ayrıca bkz. # 285523

String delim = "";
for (Item i : list) {
    sb.append(delim).append(i);
    delim = ",";
}

1
Durumu depolamanın çok şaşırtıcı bir yolu! Neredeyse OO polimorfik. Her döngüde gereksiz atamadan hoşlanmıyorum, ancak bahse girerim bir eğer'den daha etkilidir. Çoğu çözüm, doğru olmayacağını bildiğimiz bir testi tekrarlar - verimsiz olsa da, muhtemelen en net olanıdır. Bağlantı için teşekkürler!
13ren

1
Bunu beğendim, ancak akıllıca ve şaşırtıcı olduğu için kullanmanın uygun olmayacağını düşündüm (şaşırtıcı olmak iyi değil!). Ancak kendime hakim olamadım. Hala emin değilim; ancak, o kadar kısadır ki, size tüyo verecek bir yorum varsa, egzersiz yapmak kolaydır.
13ren

6
@ 13ren: Siz ve Bob Amca kod hakkında "akıllı ve şaşırtıcı" bir konuşma yapmanız gerekiyor.
Stefan Kendall

Bunu bilmeden yıllarımı boşa harcadığımı hissediyorum ^^;
Göl

Java'nın neden böyle bir görevi çirkin bir koda dönüştürmesi gerekiyor?
Eduardo

82
org.apache.commons.lang3.StringUtils.join(list,",");

1
Kaynak bulundu: svn.apache.org/viewvc/commons/proper/lang/trunk/src/java/org/… Birleştirme yönteminde 9 aşırı yükleme vardır. Yineleme tabanlı olanlar "Örnek çözüm 3" gibidir; dizin tabanlı olanlar şunları kullanır: if (i> startIndex) {<add separator>}
13ren

Yine de uygulamaların peşindeyim. Bunu bir fonksiyonda özetlemek iyi bir fikirdir, ancak pratikte, benim sınırlayıcım bazen bir satırsonu olur ve her satır da belirli bir derinliğe kadar girintilidir. Katıl (liste, "\ n" + girinti) olmadığı sürece ... bu her zaman işe yarar mı? Üzgünüm, yüksek sesle düşünüyorum.
13ren

2
Bence bu en güzel ve en kısa çözüm
Jesper Rønn-Jensen

muhtemelen list.iterator ()?
Vanuan

32

Java 8, bunu yapmanın birkaç yeni yolunu sunar:

  • joinİlgili bir yöntem String.
  • Sınıftaki joiningakışlar için bir toplayıcı Collectors.
  • StringJoinerSınıfı.

Misal:

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

Akışları kullanan yaklaşım varsayılır import static java.util.stream.Collectors.joining;.


Bu IMHO, eğer java 8 kullanıyorsanız, en iyi cevap.
scravy


7

Spring Framework kullanıyorsanız, bunu StringUtils ile yapabilirsiniz :

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)

Teşekkürler ama nasıl uygulanıyor?
13ren

6

Java'nın List toString uygulamasına göre:

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

Bunun gibi bir dilbilgisi kullanır:

List --> (Item , )* Item

İlk temelli yerine son temelli olarak, listenin sonunu kontrol etmek için aynı testle atlama virgülünü kontrol edebilir. Bunun çok zarif olduğunu düşünüyorum ama netlik konusunda emin değilim.


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

4

Java 8 kullanarak bunu başarmanın güzel bir yolu var:

List<String> list = Arrays.asList(array);
String joinedString = String.join(",", list);

TextUtils.join (",", list);
Arul Pandian

3
for(int i=0, length=list.size(); i<length; i++)
  result+=(i==0?"":", ") + list.get(i);

Evet, biraz şifreli, ancak daha önce yayınlananlara bir alternatif.
cherouvim

1
Bence kod iyi görünüyor, IMHO diğer cevabınızdan daha güzel ve neredeyse 'şifreli'. Bunu Join ve senin gülüşün denen bir yöntemle toparlayın, yine de dilin gerçekten yaygın bir sorunu çözmenin daha iyi bir yolunu bekliyorum.
tarn

@tarn: Python / perl arka planınız olduğu için iyi göründüğünü düşünüyorsunuz;) Ben de beğendim
cherouvim

3

Foreach döngüsü için bir seçenek şudur:

StringBuilder sb = new StringBuilder();
for(String s:list){
  if (sb.length()>0) sb.append(",");
  sb.append(s);
}

2
StringBuffer result = new StringBuffer();
for(Iterator it=list.iterator; it.hasNext(); ) {
  if (result.length()>0)
    result.append(", ");
  result.append(it.next());
}

Güncelleme : Dave Webb'in yorumlarda belirtildiği gibi, listedeki ilk öğeler boş dizeler ise bu doğru sonuçlar vermeyebilir.


meh .. hala döngüde eğer.
sfossen

Kısaltmak için bir foreach döngüsü kullanabilirsiniz.
Peter Lawrey

Listedeki ilk öğe boş bir dizeyse bu işe yaramaz.
Dave Webb

@Dave Webb: Evet, teşekkürler. Yine de sorunun böyle bir gerekliliği yok.
cherouvim

@cherovium: Soru, öğelerin hiçbirinin boş dize olmayacağını söylemiyor, bu yüzden bu örtük bir gereklilik. Bir CSV dosyası oluşturuyorsanız, boş alanlar oldukça yaygın olabilir.
Dave Webb

2

Genellikle bunu yaparım:

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

Java uygulamasına göre bundan sonra bu kontrolü yapacağımı düşünüyorum;)


Örnek 3 ile aynı mantık, gereksiz hiçbir şey yapmadığı için hoşuma gidiyor. Ancak, en açık olup olmadığını bilmiyorum. BTW: Liste boşken iki testten kaçınarak, süreyi if'in içine koyarsanız biraz daha etkilidir. Ayrıca benim için biraz daha anlaşılır hale getiriyor.
13ren

Göklerin aşkına, bir StringBuilder kullanmak
scravy

2

Groovy'yi kullanabiliyorsanız (JVM'de çalışır):

def list = ['a', 'b', 'c', 'd']
println list.join(',')

teşekkürler, ama uygulamalarla ilgileniyorum. Bu nasıl uygulanır?
13ren

2

(Kendi cevabımı buradan kopyalayıp yapıştırın .) Burada açıklanan çözümlerin birçoğu, özellikle dış kitaplıklara dayananlar, IMHO'nun biraz üzerinde. Her zaman kullandığım virgülle ayrılmış bir liste elde etmek için güzel, temiz ve anlaşılır bir deyim var. Koşullu (?) Operatöre dayanır:

Düzenleme : Orijinal çözüm doğru, ancak yorumlara göre optimal değil. İkinci kez denemek:

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

İşte dizi ve StringBuilder'ın bildirimi dahil olmak üzere 4 kod satırında gidiyorsunuz.

2. Düzenleme : Bir Yineleyici ile uğraşıyorsanız:

    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() ? "," : "");

Bu ve yukarıdaki stackoverflow.com/questions/668952/… benzerdir. Kısa ve net, ancak yalnızca bir yineleyiciniz varsa, önce dönüştürmeniz gerekir. Verimsiz, ancak bu yaklaşımın netliği için buna değer mi?
13ren

Ugh, StringBuilder kullanımında geçici bir StringBuilder oluşturan dize birleştirme. Yetersiz. Daha iyi (Java'yı daha çok sever, ancak daha iyi çalışan kod) "if (i> 0) {builder.append (',');}" ve ardından builder.append (array [i]) olacaktır;
Eddie

Evet, bayt koduna bakarsanız, haklısınız. Bu kadar basit bir sorunun bu kadar karmaşık olabileceğine inanamıyorum. Derleyicinin burada optimize edip edemeyeceğini merak ediyorum.
Julien Chastang

2., umarız daha iyi bir çözüm sağladı.
Julien Chastang

Bunları bölmek daha iyi olabilir (böylece insanlar birine veya diğerine oy verebilir).
13ren

1

Genellikle sürüm 3'e benzer bir şey kullanırım. İyi çalışıyor c / c ++ / bash / ...: P


1

Derlemedim ... ama çalışmalı (veya çalışmaya yakın olmalı).

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

Bu şekilde beğendim, sadece başka bir biçimlendirmeyi tercih ederim. Ancak küçük bir düzeltmeden bahsetmek gerekirse: Liste boşsa, bu bir IndexOutOfBounds istisnası atacaktır. Yani, ya setLength önce bir kontrol yapın ya da Math.max (0, ...)
tb -

1

Bu çok kısa, çok açık, ancak duyarlılıklarıma ürpertici dehşet veriyor. Ayrıca, özellikle bir String (char değil) ise, farklı sınırlayıcılara uyum sağlamak biraz garip.

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

İlham veren: Java'da geliştirilmiş for döngüsünün son yinelemesi


1
StringBuffer sb = new StringBuffer();

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

    sb.append(myList.get(i)); 
}

Bunu, statik bir fayda sınıfına yerleştirmenin çok kolay olduğunu buluyorum, aynı zamanda okuması ve anlaması da çok kolay. Ayrıca, listenin kendisi, döngü ve sınırlayıcı dışında herhangi bir ekstra değişken veya durum gerektirmez.
Joachim H. Skeie

Bu Item1Item2, Item3, Item4,

Göklerin aşkına, bir StringBuilder kullanmak
scravy

1

Bir süre önce bir blogda bulduğum bu yaklaşımı biraz beğendim. Maalesef blogun adını / URL'sini hatırlamıyorum.

Şuna benzeyen bir yardımcı / yardımcı sınıf oluşturabilirsiniz:

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

Helper sınıfını kullanmak şu kadar basittir:

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

for (String item : list) {
    sb.append(delimiter);
    sb.append(item);
}

Sadece bir not: "sınırlayıcı" değil, "sınırlayıcı" olmalıdır.
Michael Myers

1

Sınırlayıcınız "" olduğundan, aşağıdakilerden herhangi birini kullanabilirsiniz:

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

Sınırlayıcınız HERHANGİ BİR ŞEY ise, bu sizin için işe yaramayacaktır.


1

Bu çözümü beğendim:

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

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

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

StringBuilder'ı da kullanabileceğini varsayıyorum.


1

Bence okumak ve anlamak en kolay olanı:

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

0

Python'da çok kolay

",". katılın (listeniz)

C # 'da String sınıfında statik bir yöntem vardır

String.Join (",", dizinizin dizeleri)

Maalesef Java hakkında emin değilim ama diğer diller hakkında sorduğun gibi ağzımı açacağımı düşündüm. Eminim Java'da da buna benzer bir şey olacaktır.


IIRC, .Net sürümünde bir takip edin. :-(

1
Yeni test edildi, .NET veya Python'da sonda sınırlayıcı yok
tarn

1
Teşekkürler! Kaynağını buldum: svn.python.org/view/python/branches/release22-branch/Objects/… "Her şeyi katlayın" arayın. Son öğe için ayırıcıyı atlar.
13ren

@ 13ren Harika! Yardımcı olur mu? Bir kilidim vardı ve bu günlerde neden C'de programlamamayı seçtiğimi hemen hatırladım: P
tarn

@tarn, evet, bu ilginç bir örnek çünkü sadece son öğe değilse kommeni ekler: if (i <seqlen - 1) {<add separator>}
13ren

0

Sınırlayıcı dizeyi koşulsuz olarak ekleyebilir ve döngüden sonra sondaki fazla sınırlayıcıyı kaldırabilirsiniz. Daha sonra, başlangıçta bir "eğer liste boşsa ve sonra bu dizeyi döndür", sondaki kontrolden kaçınmanıza izin verecektir (boş bir listeden karakterleri kaldıramayacağınız için)

Yani asıl soru şu:

"Bir döngü ve bir eğer verildiğinde, bunları bir araya getirmenin en net yolu sizce nedir?"


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

1
Sanırım demek listToString.substring(0, listToString.length()-separator.length());
istedin

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

Dayanarak virgülle sınırlamayı listesi (Java) için En Temiz yolu?

Bu fikri kullanarak: Bir döngüdeki son öğe ayrı bir muameleyi hak ediyor mu?


1
Bunun çalışması için listede en az 1 öğe olduğunu bilmeniz gerekir, aksi takdirde ilk for-döngünüz bir IndexOutOfBoundsException ile çökecektir.
Joachim H. Skeie

@ Joachim teşekkürler, tabii ki haklısın. Ne çirkin bir çözüm yazdım! Ben değiştireceğiz for (int i=0; i<1; i++)için 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();
}


0

Şu ana kadar cevapların hiçbiri özyineleme kullanmıyor ...

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, ','));
    }

}

0
private String betweenComma(ArrayList<String> strings) {
    String united = "";
    for (String string : strings) {
        united = united + "," + string;
    }
    return united.replaceFirst(",", "");
}

-1

Yöntem

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

Kullanım

[1,2,3] dizisi verildiğinde

join(myArray, ",") // 1,2,3
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.