当我在一个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 8 之前:
Apache的commons lang在这里是你的朋友——它提供了一个连接方法,非常类似于你在Ruby中引用的方法:
StringUtils.join (java.lang.Iterable字符)
Java 8:
Java 8通过StringJoiner和String.join()提供了开箱即用的连接。下面的代码片段展示了如何使用它们:
StringJoiner
StringJoiner joiner = new StringJoiner(",");
joiner.add("01").add("02").add("03");
String joinedString = joiner.toString(); // "01,02,03"
字符串。join(CharSequence分隔符,CharSequence…元素)
String joinedString = String.join(" - ", "04", "05", "06"); // "04 - 05 - 06"
字符串。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, ",");
不知道这是否真的更好,但至少它使用了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");
}
我会使用谷歌集合。有一个很好的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);
}
}
我认为它更好地用于对象集合,因为现在你不必在加入它们之前将对象转换为字符串。
你把事情搞得更复杂了。让我们从例子的结尾开始:
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变量参数,所以你不需要将所有的字符串显式地填充到一个集合或数组中:
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"));
}
}
使用StringBuilder和类Separator
StringBuilder buf = new StringBuilder();
Separator sep = new Separator(", ");
for (String each : list) {
buf.append(sep).append(each);
}
分隔符包装分隔符。分隔符由Separator的toString方法返回,除非第一次调用返回空字符串!
类分隔符的源代码
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;
}
}
}
谷歌的Guava库有com.google.common.base.Joiner类,它可以帮助解决这样的任务。
样品:
"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"
这是一篇关于Guava的字符串实用程序的文章。
如果使用Eclipse Collections,则可以使用makeString()或appendString()。
makeString()返回String表示形式,类似于toString()。
它有三种形式
makeString(开始,分隔符,结束)
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),并且是空的。它有同样的三种形式,有一个额外的第一个参数,可追加的。
MutableList<Integer> list = FastList.newListWith(1, 2, 3);
Appendable appendable = new StringBuilder();
list.appendString(appendable, "[", "/", "]");
assertEquals("[1/2/3]", appendable.toString());
如果不能将集合转换为Eclipse Collections类型,只需使用相关的适配器对其进行调整。
List<Object> list = ...;
ListAdapter.adapt(list).makeString(",");
注意:我是Eclipse集合的提交者。
对于那些在Spring上下文中的人来说,他们的StringUtils类也很有用:
有很多有用的快捷方式,比如:
科尔collectionToCommaDelimitedString(收藏)
收集字符串(收集科尔,字符串)
弦(对象[]arr,弦delim)
还有很多其他的。
如果您还没有使用Java 8,并且已经在Spring上下文中,这将很有帮助。
对于Collection的支持,我更喜欢它而不是Apache Commons(尽管也很好),像这样更容易:
// 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);