it-swarm-vi.com

Cách tốt nhất để xây dựng một chuỗi các mục được phân tách trong Java là gì?

Khi làm việc trong một ứng dụng Java, gần đây tôi cần phải tập hợp một danh sách các giá trị được phân tách bằng dấu phẩy để chuyển sang một dịch vụ web khác mà không cần biết trước sẽ có bao nhiêu phần tử. Điều tốt nhất tôi có thể nghĩ ra khỏi đỉnh đầu là một thứ như thế này:

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

Tôi nhận ra điều này không đặc biệt hiệu quả, vì có những chuỗi được tạo ra ở khắp mọi nơi, nhưng tôi sẽ làm rõ hơn là tối ưu hóa.

Trong Ruby, tôi có thể làm một cái gì đó như thế này, mà cảm thấy thanh lịch hơn nhiều:

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

Nhưng vì Java thiếu lệnh tham gia, tôi không thể tìm ra thứ gì tương đương.

Vậy, cách tốt nhất để làm điều này trong Java là gì?

280
Sean McMains

Trước Java 8:

Commons lang của Apache là bạn của bạn ở đây - nó cung cấp một phương thức nối rất giống với phương thức bạn tham khảo trong Ruby:

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


Java 8:

Java 8 cung cấp việc tham gia ngoài hộp thông qua StringJoinerString.join(). Đoạn mã dưới đây cho thấy cách bạn có thể sử dụng chúng:

StringJoinerNAME _

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"
490
Martin Gladdish

Bạn có thể viết một phương thức tiện ích kiểu tham gia nhỏ hoạt động trên 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();
}

Sau đó sử dụng nó như vậy:

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

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

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

Trong trường hợp của Android, lớp StringUtils từ commons không khả dụng, vì vậy tôi đã sử dụng cái này

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

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

46
Kevin

thư viện Guava của Google has com.google.common.base.Joiner lớp giúp giải quyết các nhiệm vụ đó.

Mẫu:

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

Đây là một bài viết về tiện ích chuỗi của Guava .

31
Alex K

Trong Java 8, bạn có thể sử dụng String.join() :

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

Ngoài ra, hãy xem câu trả lời này để biết ví dụ về Stream API.

25
micha

Bạn có thể khái quát hóa nó, nhưng không có sự tham gia nào vào Java, như bạn nói.

Cái này có thể hoạt động tốt hơn.

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

Sử dụng một cách tiếp cận dựa trên Java.lang.StringBuilder ! ("Một chuỗi các ký tự có thể thay đổi.")

Giống như bạn đã đề cập, tất cả các nối chuỗi đó đang tạo Chuỗi trên tất cả. StringBuildersẽ không làm điều đó.

Tại sao StringBuilderthay vì StringBufferNAME _ ? Từ StringBuilderjavadoc:

Nếu có thể, lớp này được khuyến nghị sử dụng theo ưu tiên cho StringBuffer vì nó sẽ nhanh hơn trong hầu hết các triển khai.

15
Stu Thompson

trong Java 8 bạn có thể làm điều này như sau:

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

nếu danh sách có null bạn có thể sử dụng:

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

Tôi sẽ sử dụng Bộ sưu tập của Google. Có một cơ sở Nice Tham gia.
[.__.] http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/base/Join.html

Nhưng nếu tôi muốn tự viết nó,

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

Tôi nghĩ rằng nó hoạt động tốt hơn với một bộ sưu tập đối tượng, vì bây giờ bạn không phải chuyển đổi các đối tượng của mình thành chuỗi trước khi bạn tham gia chúng.

10
Eric Normand

Apache commons Lớp StringUtils có phương thức nối.

8
Alejandro

Java 8

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

Và một điều tối thiểu (nếu bạn không muốn đưa Apache Commons vs Guava vào các phụ thuộc dự án chỉ vì mục đích tham gia chuỗi)

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

Sử dụng StringBuilder và lớp Separator

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

Dấu phân cách kết thúc một dấu phân cách. Dấu phân cách được trả về bằng phương thức toString của Dấu phân cách, trừ khi trong lệnh gọi đầu tiên trả về chuỗi trống!

Mã nguồn cho lớp 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

Bạn có thể sử dụng Java StringBuilderNAME _ loại cho việc này. Cũng có StringBuffername__, nhưng nó chứa logic an toàn luồng bổ sung thường không cần thiết.

3
Kent Boogaart

Nếu bạn đang sử dụng Bộ sưu tập Eclipse , bạn có thể sử dụng makeString() hoặc appendString().

makeString() trả về đại diện String, tương tự như toString().

Nó có ba hình thức

  • makeString(start, separator, end)
  • makeString(separator) mặc định bắt đầu và kết thúc chuỗi trống
  • makeString() mặc định dấu phân cách thành ", " (dấu phẩy và dấu cách)

Mã ví dụ:

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() tương tự như makeString(), nhưng nó gắn vào một Appendable (như StringBuilder) và là void. Nó có ba hình thức giống nhau, với một đối số đầu tiên bổ sung, Phụ lục.

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

Nếu bạn không thể chuyển đổi bộ sưu tập của mình thành loại Bộ sưu tập Eclipse, chỉ cần điều chỉnh nó với bộ điều hợp có liên quan.

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

Lưu ý: Tôi là người ủy thác cho các bộ sưu tập Eclipse.

2
Craig P. Motlin

Nếu bạn đang sử dụng Spring MVC thì bạn có thể thử các bước sau.

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

Nó sẽ dẫn đến a,b,c

2
Ankit Lalan

Tại sao không viết phương thức tham gia () của riêng bạn? Nó sẽ lấy bộ sưu tập tham số của Chuỗi và Chuỗi phân cách. Trong phương thức lặp qua bộ sưu tập và xây dựng kết quả của bạn trong StringBuffer.

2
Dave Costa

Với đối số biến Java 5, do đó bạn không phải nhồi tất cả các chuỗi của mình vào một bộ sưu tập hoặc mảng một cách rõ ràng:

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

Kiểu bản địa Java 8

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

Đối tượng tùy chỉnh Java 8:

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

Đối với những người ở trong bối cảnh Mùa xuân, lớp StringUtils cũng hữu ích:

Có nhiều phím tắt hữu ích như:

  • collectionToCommaDelrictString (Bộ sưu tập coll)
  • collectionToDelrictString (Bộ sưu tập coll, Chuỗi delim)
  • arrayToDelrictString (Object [] Array, String delim)

và nhiều người khác.

Điều này có thể hữu ích nếu bạn chưa sử dụng Java 8 và bạn đã ở trong bối cảnh Spring.

Tôi thích nó hơn so với Apache Commons (mặc dù cũng rất tốt) cho hỗ trợ Bộ sưu tập dễ dàng hơn như thế này:

// 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
рüффп

Có lẽ bạn nên sử dụng một StringBuilder với phương thức append để xây dựng kết quả của bạn, nhưng nếu không thì đây là một giải pháp tốt như Java cung cấp.

1
newdayrising

Tại sao bạn không làm trong Java giống như điều bạn đang làm trong Ruby, đó là tạo chuỗi phân tách bằng dấu phân cách chỉ sau khi bạn đã thêm tất cả các phần vào mảng?

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

Bạn có thể muốn di chuyển vòng lặp đó trong một phương thức trợ giúp riêng biệt và cũng sử dụng StringBuilder thay vì StringBuffer ...

Chỉnh sửa: đã sửa thứ tự nối thêm.

1
agnul

Về cơ bản là một cái gì đó như thế này:

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

Không biết điều này có thực sự tốt hơn không, nhưng ít nhất nó đang sử dụng StringBuilder, có thể hiệu quả hơn một chút.

Dưới đây là một cách tiếp cận chung hơn nếu bạn có thể xây dựng danh sách các tham số TRƯỚC KHI thực hiện bất kỳ phân định tham số nào.

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

Cách tiếp cận của bạn không quá tệ, nhưng bạn nên sử dụng StringBuffer thay vì sử dụng dấu +. Dấu + có nhược điểm lớn là một thể hiện Chuỗi mới đang được tạo cho mỗi hoạt động đơn lẻ. Chuỗi của bạn càng dài, chi phí càng lớn. Vì vậy, sử dụng StringBuffer nên là cách nhanh nhất:

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

Sau khi hoàn thành việc tạo chuỗi của bạn, chỉ cần gọi toString () trên StringBuffer được trả về.

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

bằng cách sử dụng Dollar đơn giản như gõ:

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

NB: nó cũng hoạt động cho Array và các loại dữ liệu khác

Thực hiện

Trong nội bộ, nó sử dụng một thủ thuật rất gọn gàng:

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

lớp Separator chỉ trả về Chuỗi trống lần đầu tiên được gọi, sau đó nó trả về dấu phân cách:

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

Sửa câu trả lời Rob Dickerson.

Nó dễ sử dụng hơn:

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

Thay vì sử dụng nối chuỗi, bạn nên sử dụng StringBuilder nếu mã của bạn không được xâu chuỗi và StringBuffer nếu có.

0
Aaron

Vì vậy, một vài điều bạn có thể làm để có được cảm giác rằng dường như bạn đang tìm kiếm:

1) Mở rộng lớp List - và thêm phương thức nối vào nó. Phương thức nối chỉ đơn giản là thực hiện công việc nối và thêm dấu phân cách (có thể là một tham số cho phương thức nối)

2) Có vẻ như Java 7 sẽ thêm các phương thức mở rộng vào Java - cho phép bạn chỉ cần đính kèm một phương thức cụ thể vào một lớp: vì vậy bạn có thể viết phương thức nối đó và thêm nó như một phương thức mở rộng vào Danh sách hoặc thậm chí Bộ sưu tập.

Giải pháp 1 có lẽ là giải pháp thực tế duy nhất, bây giờ, mặc dù Java 7 chưa ra mắt :) Nhưng nó sẽ hoạt động tốt.

Để sử dụng cả hai thứ này, bạn chỉ cần thêm tất cả các mục của mình vào Danh sách hoặc Bộ sưu tập như bình thường và sau đó gọi phương thức tùy chỉnh mới để 'tham gia' chúng.

0
user13276

Bạn đang làm điều này phức tạp hơn một chút so với nó phải có. Hãy bắt đầu với phần cuối của ví dụ của bạn:

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

Với một thay đổi nhỏ khi sử dụng StringBuilder thay vì String, điều này trở thành:

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

Khi bạn đã hoàn tất (tôi giả sử bạn cũng phải kiểm tra một vài điều kiện khác), chỉ cần đảm bảo bạn xóa dấu phẩy đuôi bằng một lệnh như thế này:

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

Và cuối cùng, có được chuỗi bạn muốn với

parameterString.toString();

Bạn cũng có thể thay thế "," trong lệnh gọi thứ hai để nối bằng chuỗi phân cách chung có thể được đặt thành bất kỳ thứ gì. Nếu bạn có một danh sách những thứ bạn biết bạn cần phải nối thêm (không có điều kiện), bạn có thể đặt mã này bên trong một phương thức lấy danh sách các chuỗi.

0
cjw2600

Cải thiện nhẹ [tốc độ] của phiên bản từ 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

Cá nhân tôi khá thường xuyên sử dụng giải pháp đơn giản sau đây cho mục đích đăng nhập:

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

Bạn có thể thử một cái gì đó như thế này:

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