it-swarm.cn

在Java中构建一系列分隔项的最佳方法是什么?

在Java应用程序中工作时,我最近需要组装一个以逗号分隔的值列表,以传递给另一个Web服务,而无需事先知道有多少元素。我能想出的最好的东西是这样的:

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

我意识到这不是特别有效,因为在整个地方都会创建字符串,但我的目的是为了清晰而不是优化。

在Ruby中,我可以做这样的事情,感觉更优雅:

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

但由于Java缺少连接命令,我无法找出任何等效的东西。

那么,在Java中执行此操作的最佳方法是什么?

274
Sean McMains

前Java 8:

Apache的commons lang是你的朋友 - 它提供了一个非常类似于你在Ruby中引用的连接方法:

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


Java 8:

Java 8通过StringJoinerString.join()提供开箱即用的连接。下面的代码段显示了如何使用它们:

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

您可以编写一个适用于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();
}

然后像这样使用它:

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

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

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

在Android的情况下,来自commons的StringUtils类不可用,所以为此我使用了

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

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

44
Kevin

Google的Guava库 hascom.google.common.base.Joinerclass有助于解决此类任务。

样品:

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

这是 关于番石榴的字符串实用程序 的文章)。

31
Alex K

在Java 8中,您可以使用 String.join()

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

另请参阅 此答案 对于Stream API示例。

25
micha

您可以对它进行概括,但正如您所说,Java中没有连接。

可能会更好。

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

使用基于 Java.lang.StringBuilder的方法 ! (“一个可变的字符序列。”)

就像你提到的那样,所有这些字符串连接都在创建字符串。 StringBuilder不会这样做。

为什么StringBuilder而不是 StringBuffer ?来自StringBuilder javadoc:

在可能的情况下,建议首先使用此类优先于StringBuffer,因为在大多数实现中它会更快。

15
Stu Thompson

在Java 8中你可以这样做:

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

如果list有空值,你可以使用:

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

我会使用Google Collections。有一个Nice Join设施。
http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/base/Join.html

但如果我想自己写,

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

我认为它对象集合更好用,因为现在你不必在加入它们之前将对象转换为字符串。

10
Eric Normand

Apache commons StringUtils类有一个join方法。

8
Alejandro

Java 8

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

最小的一个(如果你不想仅仅为了加入字符串而将Apache Commons vs Guava包含在项目依赖项中)

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

使用StringBuilder和类Separator

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

分隔符包装分隔符。分隔符的toString方法返回分隔符,除非第一次调用返回空字符串!

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

您可以使用Java的 StringBuilder 类型。还有StringBuffer,但它包含额外的线程安全逻辑,这通常是不必要的。

3
Kent Boogaart

如果你正在使用 Eclipse Collections ,你可以使用makeString()appendString()

makeString()返回String表示,类似于toString()

它有三种形式

  • makeString(start, separator, end)
  • makeString(separator)默认开始和结束为空字符串
  • makeString()将分隔符默认为", "(逗号和空格)

代码示例:

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()类似于makeString(),但它附加到Appendable(如StringBuilder)并且是void。它具有相同的三种形式,附加的第一个参数是Appendable。

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

如果无法将集合转换为Eclipse集合类型,则只需使用相关适配器进行调整即可。

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

注意: 我是Eclipse集合的提交者。

2
Craig P. Motlin

如果您使用的是Spring MVC,那么您可以尝试以下步骤。

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

它将导致a,b,c

2
Ankit Lalan

为什么不编写自己的join()方法?它将作为字符串的参数集合和分隔符字符串。在该方法中迭代集合并在StringBuffer中构建结果。

2
Dave Costa

使用Java 5变量args,因此您不必将所有字符串显式填充到集合或数组中:

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 Native Type

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

Java 8自定义对象:

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

对于那些处于Spring上下文中的人,他们的 StringUtils class也很有用:

有许多有用的快捷方式,如:

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

和许多其他人。

如果您尚未使用Java 8并且已经处于Spring上下文中,这可能会有所帮助。

我更喜欢它反对Apache Commons(虽然非常好),对于Collection支持更容易这样:

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

您应该使用带有StringBuilder方法的append来构造结果,但除此之外,这与Java必须提供的解决方案一样好。

1
newdayrising

你为什么不用Java在Ruby中做同样的事情,那就是在你将所有的部分添加到数组之后才创建分隔符分隔的字符串?

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

您可能希望在单独的帮助器方法中移动for循环,并且还使用StringBuilder而不是StringBuffer ...

编辑 :修正附加顺序。

1
agnul

所以基本上是这样的:

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

不知道这是否真的更好,但至少它使用的是StringBuilder,它可能稍微高效一点。

如果您可以在执行任何参数分隔之前构建参数列表,则下面是更通用的方法。

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

你的方法并不太糟糕,但你应该使用StringBuffer而不是使用+号。 +有一个很大的缺点,即为每个操作创建一个新的String实例。字符串越长,开销就越大。所以使用StringBuffer应该是最快的方法:

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

完成字符串创建后,只需在返回的StringBuffer上调用toString()。

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

使用 Dollar 很简单,因为输入:

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

注意:它也适用于Array和其他数据类型

履行

在内部,它使用了一个非常巧妙的技巧:

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

Separator仅在第一次调用时返回空String,然后返回分隔符:

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

修复回答Rob Dickerson。

它更容易使用:

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

如果你的代码没有线程化,你应该使用StringBuilder而不是使用字符串连接,如果是的话,你应该使用StringBuffer。

0
Aaron

所以你可能会做一些事情来感觉你似乎正在寻找:

1)扩展List类 - 并向其添加join方法。 join方法只是简单地连接和添加分隔符(可以是join方法的参数)的工作

2)看起来Java 7将向Java添加扩展方法 - 它允许您只是将特定方法附加到类:因此您可以编写该连接方法并将其作为扩展方法添加到List甚至是采集。

解决方案1可能是唯一现实的,现在,虽然Java 7还没有出来:)但它应该工作得很好。

要同时使用这两个,您只需像往常一样将所有项目添加到列表或集合中,然后调用新的自定义方法以“加入”它们。

0
user13276

你让它变得有点复杂。让我们从你的例子的结尾开始:

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

使用StringBuilder而不是String的变化很小,这变为:

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

完成后(我假设您还必须检查其他一些条件),只需确保使用如下命令删除尾部逗号:

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

最后,获取您想要的字符串

parameterString.toString();

您还可以在第二次调用中替换“,”以附加可以设置为任何内容的通用分隔符字符串。如果你有一个你知道需要附加的东西的列表(非条件),你可以把这个代码放在一个带有字符串列表的方法中。

0
cjw2600

来自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

我个人经常使用以下简单的解决方案进行日志记录:

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

你可以尝试这样的事情:

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