在 Java 中构建一串分隔项的最佳方法是啥?
Posted
技术标签:
【中文标题】在 Java 中构建一串分隔项的最佳方法是啥?【英文标题】:What's the best way to build a string of delimited items in Java?在 Java 中构建一串分隔项的最佳方法是什么? 【发布时间】:2010-09-08 22:54:33 【问题描述】:在使用 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 中最好的方法是什么?
【问题讨论】:
StringbBilder 是要走的路 - java.lang.StringBuilder。 对于 Java 8 看看这个答案:***.com/a/22577623/1115554 【参考方案1】:如果您想在对象属性列表中应用逗号。这是我发现最有用的方式。
这里 getName() 是我一直在尝试将“,”添加到的类的字符串属性。
String message = listName.stream().map(list -> list.getName()).collect(Collectors.joining(", "));
【讨论】:
【参考方案2】:在 Java 8 中,您可以这样做:
list.stream().map(Object::toString)
.collect(Collectors.joining(delimiter));
如果列表有空值,您可以使用:
list.stream().map(String::valueOf)
.collect(Collectors.joining(delimiter))
它还支持前缀和后缀:
list.stream().map(String::valueOf)
.collect(Collectors.joining(delimiter, prefix, suffix));
【讨论】:
【参考方案3】:Java 8 原生类型
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(","));
【讨论】:
【参考方案4】:如果您使用的是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 Collections 类型,只需使用相关适配器对其进行调整即可。
List<Object> list = ...;
ListAdapter.adapt(list).makeString(",");
注意:我是 Eclipse 集合的提交者。
【讨论】:
【参考方案5】:使用 StringBuilder 和类Separator
StringBuilder buf = new StringBuilder();
Separator sep = new Separator(", ");
for (String each : list)
buf.append(sep).append(each);
Separator 包含一个分隔符。分隔符由 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;
【讨论】:
如何修改Separator
使其使用StringBuilder
而不是连接Strings
?
@Mohamed Separator
只返回分隔符,它不连接字符串本身。
这个类是什么Separator
???为什么不只使用一个简单的字符串..?!【参考方案6】:
如果您使用的是 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
【讨论】:
【参考方案7】:我个人经常使用以下简单的解决方案来记录日志:
List lst = Arrays.asList("ab", "bc", "cd");
String str = lst.toString().replaceAll("[\\[\\]]", "");
【讨论】:
【参考方案8】:Java 8
stringCollection.stream().collect(Collectors.joining(", "));
【讨论】:
【参考方案9】:Java 8 之前:
Apache 的 commons lang 在这里是您的朋友 - 它提供了一种与您在 Ruby 中引用的非常相似的连接方法:
StringUtils.join(java.lang.Iterable,char)
Java 8:
Java 8 通过StringJoiner
和String.join()
提供开箱即用的连接。下面的 sn-ps 显示了如何使用它们:
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"
【讨论】:
我想知道 - 这是否考虑到集合中对象的字符串表示是否包含分隔符本身? 正是我想要的:StringUtils.join(java.util.Collection,String) 来自包 org.apache.commons.lang3.StringUtils ,jar 文件是 commons-lang3-3.0.1 .jar 在 android 上你也可以使用 TextUtils.join()。 这不是最好的方法。即使集合中的对象为空/空,它也会始终添加字符。它既漂亮又干净,但有时它会打印一个双分隔符字符。 我想知道如果这样的 API 不能处理 null 和空值有什么意义【参考方案10】:修正答案 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);
【讨论】:
【参考方案11】:还有一个最小的(如果您不想仅仅为了加入字符串而将 Apache Commons 或 Gauva 包含到项目依赖项中)
/**
*
* @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();
【讨论】:
使用 delim 代替 File.separator。【参考方案12】:对于那些在 Spring 上下文中的人,他们的 StringUtils 类也很有用:
有很多有用的快捷方式,例如:
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);
【讨论】:
【参考方案13】:在 Java 8 中,您可以使用 String.join()
:
List<String> list = Arrays.asList("foo", "bar", "baz");
String joined = String.join(" and ", list); // "foo and bar and baz"
还可以查看 this answer 以获取 Stream API 示例。
【讨论】:
【参考方案14】:Google's Guava library 具有 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"
这是article about Guava's string utilities。
【讨论】:
【参考方案15】:您可以概括它,但正如您所说,Java 中没有连接。
This 可能会更好。
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();
【讨论】:
我同意这个答案,但有人可以编辑签名以使其接受 CollectionIterable<String>
并使用可以迭代它的事实。在您的示例中,您不需要集合中的项目数,因此这更加通用。
或者更好的是,使用 Iterable extends Charsequence> 然后你可以接受 StringBuilders 和 Strings 的集合以及流和其他类似字符串的东西。 :)
您想改用StringBuilder
。它们是相同的,除了StringBuffer
提供了不必要的线程安全。有人可以编辑吗!
这段代码有几个错误。 1. CharSequence 有一个大写字母 s。 2. s.iterator() 返回一个 Iterator。 3. Iterable没有isEmpty()
方法,改用next()
方法【参考方案16】:
您可以为此使用 Java 的 StringBuilder
类型。还有StringBuffer
,但它包含通常不必要的额外线程安全逻辑。
【讨论】:
【参考方案17】:在 Android 的情况下,来自 commons 的 StringUtils 类不可用,所以我使用了这个
android.text.TextUtils.join(CharSequence delimiter, Iterable tokens)
http://developer.android.com/reference/android/text/TextUtils.html
【讨论】:
【参考方案18】:来自 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();
【讨论】:
【参考方案19】:使用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
类仅在第一次调用时返回空字符串,然后返回分隔符:
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;
【讨论】:
如果我想在文件的每一行(记录)或类似的东西上使用分隔符怎么办?每次创建一个新的Separator
?在您引入属性wasCalled
的那一刻,您已经添加了线程问题。最后,$
是什么?这是一个 Java 问题。【参考方案20】:
使用 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"));
【讨论】:
【参考方案21】:使用基于java.lang.StringBuilder
的方法! ("一个可变的字符序列。")
就像你提到的,所有这些字符串连接都在创建字符串。 StringBuilder
不会那样做。
为什么是StringBuilder
而不是StringBuffer
?来自StringBuilder
javadoc:
在可能的情况下,建议优先使用此类而不是 StringBuffer,因为它在大多数实现下会更快。
【讨论】:
是的。此外,StringBuffer 是线程安全的,而 StringBuilder 不是。【参考方案22】:为什么不在 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...
编辑:修正了追加的顺序。
【讨论】:
是的,为什么要使用 StringBuffer 而不是 StringBuilder(因为您使用的是 Java 1.5+)?你也有你的附加错误。【参考方案23】:因此,您可以做几件事来获得您正在寻找的感觉:
1) 扩展 List 类 - 并为其添加 join 方法。 join 方法将简单地完成连接和添加分隔符的工作(可以是 join 方法的参数)
2) 看起来 Java 7 将向 java 添加扩展方法 - 它允许您将特定方法附加到类上:因此您可以编写该 join 方法并将其作为扩展方法添加到 List甚至收藏。
解决方案 1 可能是目前唯一现实的解决方案,尽管 Java 7 还没有推出 :) 但它应该可以正常工作。
要同时使用这两者,您只需像往常一样将所有项目添加到列表或集合中,然后调用新的自定义方法来“加入”它们。
【讨论】:
【参考方案24】://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();
【讨论】:
【参考方案25】:我会使用 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);
我认为它更适用于对象集合,因为现在您不必在加入对象之前将它们转换为字符串。
【讨论】:
【参考方案26】:你让这变得比它必须的要复杂一些。让我们从示例的结尾开始:
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();
您还可以将第二次调用中的“,”替换为可以设置为任何内容的通用分隔符字符串。如果您知道需要(无条件地)附加的内容列表,则可以将此代码放在采用字符串列表的方法中。
【讨论】:
【参考方案27】:如果你的代码不是线程化的,你应该使用 StringBuilder,如果是,则使用 StringBuffer,而不是使用字符串连接。
【讨论】:
【参考方案28】:您的方法还不错,但您应该使用 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()。
【讨论】:
在这里使用 StringBuffer 只会让它变慢,没有充分的理由。使用 + 运算符将在内部使用更快的 StringBuilder,因此他除了使用 StringBuffer 来获得他不需要的线程安全之外什么都没有。 另外...“+ 有一个很大的缺点是每个操作都会创建一个新的 String 实例”这句话是错误的。编译器会从中生成 StringBuilder.append() 调用。【参考方案29】:不知道这是否真的更好,但至少它使用了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");
【讨论】:
【参考方案30】:Apache commonsStringUtils 类有一个join方法。
【讨论】:
以上是关于在 Java 中构建一串分隔项的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
在 iPhone 上构建“查找最近”位置感知应用程序的最佳方法是啥?
构建应用程序以在单个 UITableView 中显示两个列表的最佳方法是啥(在数据集之间切换)