pengembangan-web-mp-pd.com

Apa cara terbaik untuk membangun serangkaian item terbatas di Jawa?

Saat bekerja di aplikasi Java, saya baru-baru ini perlu menyusun daftar nilai yang dipisahkan koma untuk diteruskan ke layanan web lain tanpa mengetahui berapa banyak elemen yang akan ada di muka. Yang terbaik yang bisa saya dapatkan dari atas kepala saya adalah sesuatu seperti ini:

public String appendWithDelimiter( String original, String addition, String delimiter ) {
    if ( original.equals( "" ) ) {
        return addition;
    } else {
        return original + delimiter + addition;
    }
}

String parameterString = "";
if ( condition ) parameterString = appendWithDelimiter( parameterString, "elementName", "," );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, "anotherElementName", "," );

Saya menyadari ini tidak terlalu efisien, karena ada string yang dibuat di semua tempat, tetapi saya akan mencari kejelasan lebih dari optimasi.

Di Ruby, saya bisa melakukan sesuatu seperti ini, yang terasa jauh lebih elegan:

parameterArray = [];
parameterArray << "elementName" if condition;
parameterArray << "anotherElementName" if anotherCondition;
parameterString = parameterArray.join(",");

Tapi karena Java tidak memiliki perintah join, saya tidak dapat menemukan yang setara.

Jadi, apa cara terbaik untuk melakukan ini di Jawa?

292
Sean McMains

Pra Java 8:

Apache commons lang adalah teman Anda di sini - ini menyediakan metode bergabung yang sangat mirip dengan yang Anda lihat di Ruby:

StringUtils.join(Java.lang.Iterable,char)


Java 8:

Java 8 menyediakan bergabung di luar kotak melalui StringJoiner dan String.join(). Cuplikan di bawah ini menunjukkan bagaimana Anda dapat menggunakannya:

StringJoiner

StringJoiner joiner = new StringJoiner(",");
joiner.add("01").add("02").add("03");
String joinedString = joiner.toString(); // "01,02,03"

String.join(CharSequence delimiter, CharSequence... elements))

String joinedString = String.join(" - ", "04", "05", "06"); // "04 - 05 - 06"

String.join(CharSequence delimiter, Iterable<? extends CharSequence> elements)

List<String> strings = new LinkedList<>();
strings.add("Java");strings.add("is");
strings.add("cool");
String message = String.join(" ", strings);
//message returned is: "Java is cool"
503
Martin Gladdish

Anda bisa menulis sedikit metode utilitas gaya gabungan yang berfungsi di Java.util.Lists

public static String join(List<String> list, String delim) {

    StringBuilder sb = new StringBuilder();

    String loopDelim = "";

    for(String s : list) {

        sb.append(loopDelim);
        sb.append(s);            

        loopDelim = delim;
    }

    return sb.toString();
}

Kemudian gunakan seperti ini:

    List<String> list = new ArrayList<String>();

    if( condition )        list.add("elementName");
    if( anotherCondition ) list.add("anotherElementName");

    join(list, ",");
52
Rob Dickerson

Dalam kasus Android, kelas StringUtils dari commons tidak tersedia, jadi untuk ini saya gunakan

Android.text.TextUtils.join(CharSequence delimiter, Iterable tokens)

http://developer.Android.com/reference/Android/text/TextUtils.html

47
Kevin

Perpustakaan Guava Google memiliki com.google.common.base.Joiner kelas yang membantu menyelesaikan tugas-tugas tersebut.

Sampel:

"My pets are: " + Joiner.on(", ").join(Arrays.asList("rabbit", "parrot", "dog")); 
// returns "My pets are: rabbit, parrot, dog"

Joiner.on(" AND ").join(Arrays.asList("field1=1" , "field2=2", "field3=3"));
// returns "field1=1 AND field2=2 AND field3=3"

Joiner.on(",").skipNulls().join(Arrays.asList("London", "Moscow", null, "New York", null, "Paris"));
// returns "London,Moscow,New York,Paris"

Joiner.on(", ").useForNull("Team held a draw").join(Arrays.asList("FC Barcelona", "FC Bayern", null, null, "Chelsea FC", "AC Milan"));
// returns "FC Barcelona, FC Bayern, Team held a draw, Team held a draw, Chelsea FC, AC Milan"

Berikut ini adalah artikel tentang utilitas string Guava .

31
Alex K

Dalam Java 8 Anda dapat menggunakan String.join() :

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

Lihat juga jawaban ini untuk contoh Stream API.

25
micha

Anda dapat menggeneralisasikannya, tetapi tidak ada yang bergabung di Jawa, seperti yang Anda katakan.

Ini mungkin bekerja lebih baik.

public static String join(Iterable<? extends CharSequence> s, String delimiter) {
    Iterator<? extends CharSequence> iter = s.iterator();
    if (!iter.hasNext()) return "";
    StringBuilder buffer = new StringBuilder(iter.next());
    while (iter.hasNext()) buffer.append(delimiter).append(iter.next());
    return buffer.toString();
}
20
Vinko Vrsalovic

Gunakan pendekatan berdasarkan Java.lang.StringBuilder ! ("Urutan karakter yang bisa berubah.")

Seperti yang Anda sebutkan, semua rangkaian string itu menciptakan Strings di seluruh. StringBuilder tidak akan melakukan itu.

Mengapa StringBuilder bukan StringBuffer ? Dari javadoc StringBuilder:

Jika memungkinkan, disarankan agar kelas ini digunakan dalam preferensi untuk StringBuffer karena akan lebih cepat di sebagian besar implementasi.

15
Stu Thompson

di Java 8 Anda dapat melakukan ini seperti:

list.stream().map(Object::toString)
                        .collect(Collectors.joining(delimiter));

jika daftar memiliki nol Anda dapat menggunakan:

list.stream().map(String::valueOf)
                .collect(Collectors.joining(delimiter))
14
gladiator

Saya akan menggunakan Google Collections. Ada fasilitas Nice Join.
http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/base/Join.html

Tetapi jika saya ingin menulisnya sendiri,

package util;

import Java.util.ArrayList;
import Java.util.Iterable;
import Java.util.Collections;
import Java.util.Iterator;

public class Utils {
    // accept a collection of objects, since all objects have toString()
    public static String join(String delimiter, Iterable<? extends Object> objs) {
        if (objs.isEmpty()) {
            return "";
        }
        Iterator<? extends Object> iter = objs.iterator();
        StringBuilder buffer = new StringBuilder();
        buffer.append(iter.next());
        while (iter.hasNext()) {
            buffer.append(delimiter).append(iter.next());
        }
        return buffer.toString();
    }

    // for convenience
    public static String join(String delimiter, Object... objs) {
        ArrayList<Object> list = new ArrayList<Object>();
        Collections.addAll(list, objs);
        return join(delimiter, list);
    }
}

Saya pikir ini berfungsi lebih baik dengan koleksi objek, karena sekarang Anda tidak perlu mengubah objek Anda menjadi string sebelum Anda bergabung dengan mereka.

10
Eric Normand

Apache commons Kelas StringUtils memiliki metode bergabung.

8
Alejandro

Java 8

stringCollection.stream().collect(Collectors.joining(", "));
5
gstackoverflow

Dan yang minimal (jika Anda tidak ingin memasukkan Apache Commons vs Guava ke dalam dependensi proyek hanya untuk bergabung dengan string)

/**
 *
 * @param delim : String that should be kept in between the parts
 * @param parts : parts that needs to be joined
 * @return  a String that's formed by joining the parts
 */
private static final String join(String delim, String... parts) {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < parts.length - 1; i++) {
        builder.append(parts[i]).append(delim);
    }
    if(parts.length > 0){
        builder.append(parts[parts.length - 1]);
    }
    return builder.toString();
}
3
Thamme Gowda

Gunakan StringBuilder dan kelas Separator

StringBuilder buf = new StringBuilder();
Separator sep = new Separator(", ");
for (String each : list) {
    buf.append(sep).append(each);
}

Pemisah membungkus pembatas. Pembatas dikembalikan oleh metode toString Separator, kecuali pada panggilan pertama yang mengembalikan string kosong!

Kode sumber untuk kelas Separator

public class Separator {

    private boolean skipFirst;
    private final String value;

    public Separator() {
        this(", ");
    }

    public Separator(String value) {
        this.value = value;
        this.skipFirst = true;
    }

    public void reset() {
        skipFirst = true;
    }

    public String toString() {
        String sep = skipFirst ? "" : value;
        skipFirst = false;
        return sep;
    }

}
3
akuhn

Anda dapat menggunakan tipe Java StringBuilder untuk ini. Ada juga StringBuffer, tetapi ini berisi logika keselamatan thread tambahan yang seringkali tidak perlu.

3
Kent Boogaart

Jika Anda menggunakan Eclipse Collections , Anda dapat menggunakan makeString() atau appendString().

makeString() mengembalikan representasi String, mirip dengan toString().

Itu memiliki tiga bentuk

  • makeString(start, separator, end)
  • makeString(separator) default memulai dan mengakhiri string kosong
  • makeString() default pemisah untuk ", " (koma dan spasi)

Contoh kode:

MutableList<Integer> list = FastList.newListWith(1, 2, 3);
assertEquals("[1/2/3]", list.makeString("[", "/", "]"));
assertEquals("1/2/3", list.makeString("/"));
assertEquals("1, 2, 3", list.makeString());
assertEquals(list.toString(), list.makeString("[", ", ", "]"));

appendString() mirip dengan makeString(), tetapi ditambahkan ke Appendable (seperti StringBuilder) dan void. Ini memiliki tiga bentuk yang sama, dengan argumen pertama tambahan, Appendable.

MutableList<Integer> list = FastList.newListWith(1, 2, 3);
Appendable appendable = new StringBuilder();
list.appendString(appendable, "[", "/", "]");
assertEquals("[1/2/3]", appendable.toString());

Jika Anda tidak dapat mengonversi koleksi Anda ke jenis Eclipse Collections, cukup sesuaikan dengan adaptor yang relevan.

List<Object> list = ...;
ListAdapter.adapt(list).makeString(",");

Catatan: Saya seorang committer untuk koleksi Eclipse.

2
Craig P. Motlin

Jika Anda menggunakan Spring MVC maka Anda dapat mencoba langkah-langkah berikut.

import org.springframework.util.StringUtils;

List<String> groupIds = new List<String>;   
groupIds.add("a");    
groupIds.add("b");    
groupIds.add("c");

String csv = StringUtils.arrayToCommaDelimitedString(groupIds.toArray());

Ini akan menghasilkan a,b,c

2
Ankit Lalan

Mengapa tidak menulis metode join () Anda sendiri? Itu akan mengambil sebagai kumpulan parameter String dan String pembatas. Di dalam metode iterate atas koleksi dan membangun hasil Anda di StringBuffer.

2
Dave Costa

Dengan Java 5 argumen variabel, jadi Anda tidak perlu memasukkan semua string Anda ke dalam koleksi atau array secara eksplisit:

import junit.framework.Assert;
import org.junit.Test;

public class StringUtil
{
    public static String join(String delim, String... strings)
    {
        StringBuilder builder = new StringBuilder();

        if (strings != null)
        {
            for (String str : strings)
            {
                if (builder.length() > 0)
                {
                    builder.append(delim).append(" ");
                }
                builder.append(str);
            }
        }           
        return builder.toString();
    }
    @Test
    public void joinTest()
    {
        Assert.assertEquals("", StringUtil.join(",", null));
        Assert.assertEquals("", StringUtil.join(",", ""));
        Assert.assertEquals("", StringUtil.join(",", new String[0]));
        Assert.assertEquals("test", StringUtil.join(",", "test"));
        Assert.assertEquals("foo, bar", StringUtil.join(",", "foo", "bar"));
        Assert.assertEquals("foo, bar, x", StringUtil.join(",", "foo", "bar", "x"));
    }
}
1
Mark B

Java 8 Jenis Asli

List<Integer> example;
example.add(1);
example.add(2);
example.add(3);
...
example.stream().collect(Collectors.joining(","));

Java 8 Custom Object:

List<Person> person;
...
person.stream().map(Person::getAge).collect(Collectors.joining(","));
1
Luis Aguilar

Bagi mereka yang berada dalam konteks Spring mereka StringUtils kelas berguna juga:

Ada banyak cara pintas yang bermanfaat seperti:

  • collectionToCommaDelimitedString (Koleksi coll)
  • collectionToDelimitedString (Koleksi coll, String delim)
  • arrayToDelimitedString (Object [] arr, String delim)

dan banyak lagi.

Ini bisa membantu jika Anda belum menggunakan Java 8 dan Anda sudah berada dalam konteks Musim Semi.

Saya lebih suka melawan Apache Commons (walaupun sangat bagus juga) untuk dukungan Koleksi yang lebih mudah seperti ini:

// Encoding Set<String> to String delimited 
String asString = org.springframework.util.StringUtils.collectionToDelimitedString(codes, ";");

// Decoding String delimited to Set
Set<String> collection = org.springframework.util.StringUtils.commaDelimitedListToSet(asString);
1
рüффп

Anda mungkin harus menggunakan StringBuilder dengan metode append untuk membangun hasil Anda, tetapi sebaliknya ini adalah solusi yang bagus seperti yang ditawarkan Java.

1
newdayrising

Mengapa Anda tidak melakukannya di Java hal yang sama dengan yang Anda lakukan di Ruby, yaitu membuat string yang dipisahkan pembatas hanya setelah Anda menambahkan semua bagian ke array?

ArrayList<String> parms = new ArrayList<String>();
if (someCondition) parms.add("someString");
if (anotherCondition) parms.add("someOtherString");
// ...
String sep = ""; StringBuffer b = new StringBuffer();
for (String p: parms) {
    b.append(sep);
    b.append(p);
    sep = "yourDelimiter";
}

Anda mungkin ingin memindahkannya untuk loop dalam metode pembantu terpisah, dan juga menggunakan StringBuilder alih-alih StringBuffer ...

Edit: memperbaiki urutan penambahan.

1
agnul

Jadi pada dasarnya sesuatu seperti ini:

public static String appendWithDelimiter(String original, String addition, String delimiter) {

if (original.equals("")) {
    return addition;
} else {
    StringBuilder sb = new StringBuilder(original.length() + addition.length() + delimiter.length());
        sb.append(original);
        sb.append(delimiter);
        sb.append(addition);
        return sb.toString();
    }
}
0
killdash10

Tidak tahu apakah ini benar-benar lebih baik, tetapi setidaknya itu menggunakan StringBuilder, yang mungkin sedikit lebih efisien.

Di bawah ini adalah pendekatan yang lebih umum jika Anda dapat membangun daftar parameter SEBELUM melakukan pembatasan parameter apa pun.

// Answers real question
public String appendWithDelimiters(String delimiter, String original, String addition) {
    StringBuilder sb = new StringBuilder(original);
    if(sb.length()!=0) {
        sb.append(delimiter).append(addition);
    } else {
        sb.append(addition);
    }
    return sb.toString();
}


// A more generic case.
// ... means a list of indeterminate length of Strings.
public String appendWithDelimitersGeneric(String delimiter, String... strings) {
    StringBuilder sb = new StringBuilder();
    for (String string : strings) {
        if(sb.length()!=0) {
            sb.append(delimiter).append(string);
        } else {
            sb.append(string);
        }
    }

    return sb.toString();
}

public void testAppendWithDelimiters() {
    String string = appendWithDelimitersGeneric(",", "string1", "string2", "string3");
}
0
Mikezx6r

Pendekatan Anda tidak terlalu buruk, tetapi Anda harus menggunakan StringBuffer alih-alih menggunakan tanda +. + Memiliki kelemahan besar bahwa instance String baru sedang dibuat untuk setiap operasi tunggal. Semakin lama string Anda, semakin besar biaya overhead. Jadi menggunakan StringBuffer harus menjadi cara tercepat:

public StringBuffer appendWithDelimiter( StringBuffer original, String addition, String delimiter ) {
        if ( original == null ) {
                StringBuffer buffer = new StringBuffer();
                buffer.append(addition);
                return buffer;
        } else {
                buffer.append(delimiter);
                buffer.append(addition);
                return original;
        }
}

Setelah Anda selesai membuat string, cukup panggil toString () pada StringBuffer yang dikembalikan.

0
Yaba
//Note: if you have access to Java5+, 
//use StringBuilder in preference to StringBuffer.  
//All that has to be replaced is the class name.  
//StringBuffer will work in Java 1.4, though.

appendWithDelimiter( StringBuffer buffer, String addition, 
    String delimiter ) {
    if ( buffer.length() == 0) {
        buffer.append(addition);
    } else {
        buffer.append(delimiter);
        buffer.append(addition);
    }
}


StringBuffer parameterBuffer = new StringBuffer();
if ( condition ) { 
    appendWithDelimiter(parameterBuffer, "elementName", "," );
}
if ( anotherCondition ) {
    appendWithDelimiter(parameterBuffer, "anotherElementName", "," );
}

//Finally, to return a string representation, call toString() when returning.
return parameterBuffer.toString(); 
0
MetroidFan2002

menggunakan Dolar sederhana seperti mengetik:

String joined = $(aCollection).join(",");

NB: berfungsi juga untuk Array dan tipe data lainnya

Penerapan

Secara internal ia menggunakan trik yang sangat rapi:

@Override
public String join(String separator) {
    Separator sep = new Separator(separator);
    StringBuilder sb = new StringBuilder();

    for (T item : iterable) {
        sb.append(sep).append(item);
    }

    return sb.toString();
}

the class Separator mengembalikan String yang kosong hanya saat pertama kali dipanggil, lalu mengembalikan separator:

class Separator {

    private final String separator;
    private boolean wasCalled;

    public Separator(String separator) {
        this.separator = separator;
        this.wasCalled = false;
    }

    @Override
    public String toString() {
        if (!wasCalled) {
            wasCalled = true;
            return "";
        } else {
            return separator;
        }
    }
}
0
dfa

Perbaiki jawaban Rob Dickerson.

Lebih mudah digunakan:

public static String join(String delimiter, String... values)
{
    StringBuilder stringBuilder = new StringBuilder();

    for (String value : values)
    {
        stringBuilder.append(value);
        stringBuilder.append(delimiter);
    }

    String result = stringBuilder.toString();

    return result.isEmpty() ? result : result.substring(0, result.length() - 1);
}
0
maXp

Alih-alih menggunakan penggabungan string, Anda harus menggunakan StringBuilder jika kode Anda tidak di-threaded, dan StringBuffer jika benar.

0
Aaron

Jadi, beberapa hal yang mungkin Anda lakukan untuk merasakan bahwa sepertinya Anda sedang mencari:

1) Perluas kelas Daftar - dan tambahkan metode bergabung ke dalamnya. Metode join hanya akan melakukan pekerjaan menggabungkan dan menambahkan pembatas (yang bisa menjadi param untuk metode join)

2) Sepertinya Java 7 akan menambahkan metode ekstensi ke Java - yang memungkinkan Anda untuk melampirkan metode tertentu ke kelas: sehingga Anda dapat menulis metode bergabung itu dan menambahkannya sebagai metode ekstensi ke Daftar atau bahkan ke Koleksi.

Solusi 1 mungkin adalah satu-satunya yang realistis, sekarang, karena Java 7 belum keluar :) Tapi itu akan berfungsi dengan baik.

Untuk menggunakan keduanya, Anda hanya perlu menambahkan semua item Anda ke Daftar atau Koleksi seperti biasa, dan kemudian memanggil metode kustom baru untuk 'bergabung' dengan mereka.

0
user13276

Anda membuat ini sedikit lebih rumit dari yang seharusnya. Mari kita mulai dengan akhir dari contoh Anda:

String parameterString = "";
if ( condition ) parameterString = appendWithDelimiter( parameterString, "elementName", "," );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, "anotherElementName", "," );

Dengan sedikit perubahan menggunakan StringBuilder alih-alih sebuah String, ini menjadi:

StringBuilder parameterString = new StringBuilder();
if (condition) parameterString.append("elementName").append(",");
if (anotherCondition) parameterString.append("anotherElementName").append(",");
...

Setelah selesai (saya berasumsi Anda harus memeriksa beberapa kondisi lain juga), pastikan Anda menghapus koma tailing dengan perintah seperti ini:

if (parameterString.length() > 0) 
    parameterString.deleteCharAt(parameterString.length() - 1);

Dan akhirnya, dapatkan string yang Anda inginkan

parameterString.toString();

Anda juga dapat mengganti "," di panggilan kedua untuk menambahkan dengan string pembatas umum yang dapat diatur ke apa saja. Jika Anda memiliki daftar hal-hal yang Anda tahu perlu Anda tambahkan (tanpa syarat), Anda bisa memasukkan kode ini ke dalam metode yang mengambil daftar string.

0
cjw2600

Peningkatan [kecepatan] versi dari izb:

public static String join(String[] strings, char del)
{
    StringBuilder sb = new StringBuilder();
    int len = strings.length;

    if(len > 1) 
    {
       len -= 1;
    }else
    {
       return strings[0];
    }

    for (int i = 0; i < len; i++)
    {
       sb.append(strings[i]).append(del);
    }

    sb.append(strings[i]);

    return sb.toString();
}
0
java al

Saya pribadi cukup sering menggunakan solusi sederhana berikut untuk tujuan logging:

List lst = Arrays.asList("ab", "bc", "cd");
String str = lst.toString().replaceAll("[\\[\\]]", "");
0

Anda dapat mencoba sesuatu seperti ini:

StringBuilder sb = new StringBuilder();
if (condition) { sb.append("elementName").append(","); }
if (anotherCondition) { sb.append("anotherElementName").append(","); }
String parameterString = sb.toString();
0
martinatime