用Java构建带分隔符的字符串的最佳方法是什么?

时间:2020-03-05 18:53:33  来源:igfitidea点击:

在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缺少join命令,所以我找不到任何等效的东西。

那么,用Java做到这一点的最佳方法是什么?

解决方案

回答

使用基于java.lang.StringBuilder的方法! ("可变字符序列。")

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

为什么使用StringBuilder而不是StringBuffer?从StringBuilderjavadoc:

Where possible, it is recommended that this class be used in preference to StringBuffer as it will be faster under most implementations.

回答

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

回答

为什么不编写自己的join()方法?它会将字符串和定界符字符串作为参数集合。在方法中,遍历集合,并在StringBuffer中建立结果。

回答

我们可以将其概括化,但是正如我们所说的,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();
}

回答

我们可以尝试如下操作:

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

回答

public static String join(String[] strings, char del)
{
    StringBuffer sb = new StringBuffer();
    int len = strings.length;
    boolean appended = false;
    for (int i = 0; i < len; i++)
    {
        if (appended)
        {
            sb.append(del);
        }
        sb.append(""+strings[i]);
        appended = true;
    }
    return sb.toString();
}

回答

我们可能应该将StringBuilderappend方法一起使用来构造结果,但是否则,这与Java必须提供的解决方案一样好。

回答

为什么在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 ...

编辑:固定追加的顺序。

回答

Java 8之前的版本:

Apache的commons lang是朋友,它提供了一种与我们在Ruby中引用的方法非常相似的join方法:

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分隔符,CharSequence ...元素))

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

`String.join(CharSequence分隔符,Iterable <?扩展CharSequence>元素)

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"

回答

我们可以编写一个适用于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, ",");

回答

所以基本上是这样的:

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

回答

Apache Commons StringUtils类具有一个join方法。

回答

不知道这是否真的更好,但是至少它使用的是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");
}

回答

方法还不错,但是我们应该使用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()。

回答

我会使用Google收藏夹。有一个不错的加入工具。
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);
    }
}

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

回答

如果代码未线程化,则应使用StringBuilder,如果不是,则应使用StringBuffer,而不是使用字符串连接。

回答

我们正在使它变得比必需的更为复杂。让我们从示例的结尾开始:

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

我们也可以在第二个调用中替换",",以添加可以设置为任何内容的通用分隔符字符串。如果我们知道需要添加的事物列表(无条件地),则可以将此代码放在采用字符串列表的方法中。

回答

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

回答

使用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)扩展List类并向其中添加join方法。 join方法将简单地进行串联和添加定界符的工作(这可能是join方法的一个参数)

2)看来Java 7将向Java添加扩展方法,从而允许我们仅将特定方法添加到类上:因此我们可以编写该join方法并将其作为扩展方法添加到List或者Collection中。

尽管Java 7尚未发布,但解决方案1可能是目前唯一可行的解​​决方案:)但它应该可以正常工作。

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

回答

使用StringBuilder和类Separator

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

分隔符包装定界符。分隔符由Separator的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;
    }

}

回答

使用Dollar就像输入一样简单:

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

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

执行

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

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

回答

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