连接 String 对象的 List 的最佳方式?

连接 String 对象列表的最佳方式是什么? 我正在考虑这样做:

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


// add elements


if (sList != null)
{
String listString = sList.toString();
listString = listString.subString(1, listString.length() - 1);
}

我发现这比使用 StringBuilder/StringBuffer 方法更简洁。

有什么想法或意见吗?

312483 次浏览

不知怎么的,我觉得这个比 使用 StringBuilder/StringBuffer 接近。

我想这取决于你采取的方法。

AbstractCollection # toString ()方法只是迭代所有元素并将它们附加到 StringBuilder。因此,您的方法可能会节省几行代码,但代价是额外的 String 操作。这种权衡是否合理,取决于你自己。

根据对性能的需求和要添加的元素数量,这可能是一个不错的解决方案。如果元素的数量很高,那么 Arraylist对内存的重新分配可能会比 StringBuilder慢一些。

ArrayList继承了 AbstractCollectiontoString()方法,即:

public String toString() {
Iterator<E> i = iterator();
if (! i.hasNext())
return "[]";


StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = i.next();
sb.append(e == this ? "(this Collection)" : e);
if (! i.hasNext())
return sb.append(']').toString();
sb.append(", ");
}
}

自己构建字符串的效率要高得多。


如果你真的想在某种 List 中预先聚合字符串,你应该提供你自己的方法来有效地加入它们,例如:

static String join(Collection<?> items, String sep) {
if(items.size() == 0)
return "";


String[] strings = new String[items.size()];
int length = sep.length() * (items.size() - 1);


int idx = 0;
for(Object item : items) {
String str = item.toString();
strings[idx++] = str;
length += str.length();
}


char[] chars = new char[length];
int pos = 0;


for(String str : strings) {
str.getChars(0, str.length(), chars, pos);
pos += str.length();


if(pos < length) {
sep.getChars(0, sep.length(), chars, pos);
pos += sep.length();
}
}


return new String(chars);
}

你看过这篇代码恐怖博客吗?

微观优化剧场的悲剧

我不确定它是否“整洁”,但从性能的角度来看,它可能不会有太大的影响。

您的方法依赖于 Java 的 ArrayList # toString ()实现。

虽然 JavaAPI 中记录了这个实现,而且不太可能改变,但是它有可能改变。自己实现这一点要可靠得多(循环、 StringBuilders、递归等任何你更喜欢的东西)。

当然,这种方法可能看起来“更整洁”或更“太甜蜜”或“金钱”,但在我看来,这是一个更糟糕的方法。

在我看来,StringBuilder 将是快速和高效的。

基本形式如下:

public static String concatStrings(List<String> strings)
{
StringBuilder sb = new StringBuilder();
for(String s: strings)
{
sb.append(s);
}
return sb.toString();
}

如果过于简单(可能的确如此) ,可以使用类似的方法添加一个分隔符,如下所示:

    public static String concatStringsWSep(List<String> strings, String separator)
{
StringBuilder sb = new StringBuilder();
for(int i = 0; i < strings.size(); i++)
{
sb.append(strings.get(i));
if(i < strings.size() - 1)
sb.append(separator);
}
return sb.toString();
}

我同意其他人对这个问题的回答,他们说不应该依赖 Java 的 ArrayList 的 toString ()方法。

代码定义答案的变体

public static String concatStringsWSep(Iterable<String> strings, String separator) {
StringBuilder sb = new StringBuilder();
String sep = "";
for(String s: strings) {
sb.append(sep).append(s);
sep = separator;
}
return sb.toString();
}

使用 Apache Commons Lang 中的 StringUtils.join方法之一。

import org.apache.commons.lang3.StringUtils;


String result = StringUtils.join(list, ", ");

如果您有幸使用 Java8,那么使用 串起来就更简单了

String result = String.join(", ", list);

使用 函数式 Java库,导入以下内容:

import static fj.pre.Monoid.stringMonoid;
import static fj.data.List.list;
import fj.data.List;

然后你可以这样做:

List<String> ss = list("foo", "bar", "baz");
String s = stringMonoid.join(ss, ", ");

或者,通常的方法是,如果您没有一个字符串列表:

public static <A> String showList(List<A> l, Show<A> s) {
return stringMonoid.join(l.map(s.showS_()), ", ");
}

假设仅仅移动一个指针/设置一个字节为 null (或者 Java 实现 StringBuilder # setLlength)会更快,而不是每次通过循环检查一个条件来查看何时追加分隔符,你可以使用这个方法:

public static String Intersperse (Collection<?> collection, String delimiter)
{
StringBuilder sb = new StringBuilder ();
for (Object item : collection)
{
if (item == null) continue;
sb.append (item).append (delimiter);
}
sb.setLength (sb.length () - delimiter.length ());
return sb.toString ();
}

Guava 是一个非常整洁的谷歌图书馆:

Joiner joiner = Joiner.on(", ");
joiner.join(sList);

下一个变化彼得劳里的答案没有初始化一个新的字符串每个循环转

String concatList(List<String> sList, String separator)
{
Iterator<String> iter = sList.iterator();
StringBuilder sb = new StringBuilder();


while (iter.hasNext())
{
sb.append(iter.next()).append( iter.hasNext() ? separator : "");
}
return sb.toString();
}

使用 Java8 +

String str = list.stream().collect(Collectors.joining())

甚至

String str = String.join("", list);

我更喜欢 Java8中的 String.join (list)

如果您正在为 Android 开发,那么 SDK 提供了 TextUtils.join

如果您的依赖项中有 json,则可以使用 new JSONArray(list).toString()

在 java 8中,你也可以使用 rereduce,比如:

public static String join(List<String> strings, String joinStr) {
return strings.stream().reduce("", (prev, cur) -> prev += (cur + joinStr));
}

如果你使用的是 java 8,你可以编写一行程序,而不是依赖于 ArrayList.toString()的实现:

String result = sList.stream()
.reduce("", String::concat);

如果您喜欢使用 StringBuffer而不是 String,因为 String::concat的运行时为 O(n^2),那么您可以首先将每个 String转换为 StringBuffer

StringBuffer result = sList.stream()
.map(StringBuffer::new)
.reduce(new StringBuffer(""), StringBuffer::append);

这是我迄今为止发现的最优雅、最干净的方式:

list.stream().collect(Collectors.joining(delimiter));