连接字符串对象列表的最佳方法? [复制]
Posted
技术标签:
【中文标题】连接字符串对象列表的最佳方法? [复制]【英文标题】:Best way to concatenate List of String objects? [duplicate] 【发布时间】:2010-10-06 03:35:50 【问题描述】:连接 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 方法更简洁。
有什么想法/cmets?
【问题讨论】:
【参考方案1】:使用 Apache Commons Lang 中的 StringUtils.join 方法之一。
import org.apache.commons.lang3.StringUtils;
String result = StringUtils.join(list, ", ");
如果您有幸使用 Java 8,那就更简单了...只需使用 String.join
String result = String.join(", ", list);
【讨论】:
Guava 用户的备选方案:Joiner.on(', ').join(Collection)
是的,每次:Bloch,“Effective Java”,第 47 项:“了解和使用库”。 Apache Commons 库应该是放入构建文件的第一件事(希望是 Gradle)。正如第 47 条总结的那样:“总而言之,不要重新发明***”。
使用 Guava API:Joiner.on(', ').skipNulls().join(Iterable<?>)
。要表示空值,您可以使用 Joiner.on(',').useForNull("#").join(Iterable<?>)
@Gojir4 也许你应该在使用类似语言之前检查 java 版本 >8,请参阅副本
从 Java 8 开始,最好使用 String#join(不需要库)。【参考方案2】:
使用 Java 8+
String str = list.stream().collect(Collectors.joining())
甚至
String str = String.join("", list);
【讨论】:
【参考方案3】:您的方法依赖于 Java 的 ArrayList#toString() 实现。
虽然在 Java API 中记录了实现并且不太可能改变,但它有可能改变。自己实现这一点要可靠得多(循环、StringBuilders、递归任何你喜欢的东西)。
当然,这种方法可能看起来“更整洁”或更“太甜”或“钱”,但在我看来,这是一种更糟糕的方法。
【讨论】:
我同意我不喜欢 OP 的做法,但出于不同的原因。 (1) 这样做会掩盖代码的意图(而像StringUtils.join
这样的东西是完全可读的)。 (2) 它不灵活,因为您可能想要更改分隔符。 (只是在最后一个字符串中替换", "
不是一个好方法,因为原始字符串中可能嵌入了", "
。)所以即使,正如你所说,ArrayList.toString
可能不会改变,我仍然不会走这条路。
您可以 99.9% 确定 ArrayList.toString 不会改变,除非它以某种方式成为攻击向量;向后兼容性将确保这一点。但我同意使用 toString() 仍然是个坏主意。【参考方案4】:
codefin 答案的变体
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();
【讨论】:
@codefine 见***.com/questions/58431/… 我真的很喜欢这个答案 b/c 你可以使用 foreach ,它非常简单,但效率也更低。你怎样才能使它更紧密的循环? 字符串不需要使用List。收藏会更灵活 @lilalinux 改为 Iterable。 在第一轮迭代后替换 sep 的非常巧妙的技巧。【参考方案5】:如果你是为android开发,SDK提供了TextUtils.join
。
【讨论】:
【参考方案6】:这是迄今为止我发现的最优雅、最干净的方式:
list.stream().collect(Collectors.joining(delimiter));
【讨论】:
IntelliJ 给了我一个提示,我可以通过将此解决方案从其他答案更改为 String.join 示例来改进事情,理由如下“报告流 API 调用链可以简化。它允许避免遍历集合时创建冗余临时对象。”【参考方案7】:Guava 是来自 Google 的一个非常简洁的库:
Joiner joiner = Joiner.on(", ");
joiner.join(sList);
【讨论】:
【参考方案8】:我更喜欢 Java 8 中的 String.join(list)
【讨论】:
【参考方案9】:你看过这个 Coding Horror 博客文章吗?
The Sad Tragedy of Micro-Optimization Theater
我不确定它是否“更整洁”,但从性能的角度来看,它可能并不重要。
【讨论】:
【参考方案10】:在我看来,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() 方法。
【讨论】:
【参考方案11】:ArrayList
从AbstractCollection
继承其toString()
-方法,即:
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(", ");
自己构建字符串会更有效率。
如果您真的想预先将字符串聚合到某种列表中,您应该提供自己的方法来有效地加入它们,例如像这样:
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);
【讨论】:
我希望你做了基准测试来确认你的实现更好,因为它并不比 AbstractCollection 的 toString 实现更干净。读者必须真正专注于您的实施才能理解某些内容,而其他内容则非常容易阅读。【参考方案12】:不知何故,我发现这比 使用 StringBuilder/StringBuffer 接近。
我想这取决于您采取的方法。
AbstractCollection#toString() 方法简单地遍历所有元素并将它们附加到 StringBuilder。因此,您的方法可能会节省几行代码,但代价是额外的字符串操作。这种权衡是否合适取决于您。
【讨论】:
您所说的额外字符串操作是在末尾创建子字符串,对吧? @Jagmal,是的。如果这段代码运行很多,那么你应该分析它是否对你有问题。否则你应该没问题。要记住的一件事是内部 toString() 的格式可能会在未来的 JDK 版本中发生变化。 @Kevin:是的,我想,在未来的 JDK 版本中更改 toString 的实现听起来足够好,我认为不这样做的理由。 :) @Jagmal:subString 真的在伤害你。如果您的 listString 占用 n 个字节,则 subString 临时将另外 n-4 个字节添加到堆中。通过使用 StringBuilder,您将从“第一次尝试”中收到您的 n-4 @LicenseQ:我不知道在您编写此声明时是否正确,但对于最近发布的任何 Java 版本都不正确。 String.substring 的实现比这更聪明:它不复制原始字符串的字符缓冲区,它只是创建一个指向同一缓冲区的新 String 对象,并使用非零“偏移”字段来指示字符串不t 从缓冲区的开头开始。 Substring 分配一个 String 对象,其中包含三个 int(count、hash 和 offset)和对预先存在的缓冲区的引用,仅此而已。【参考方案13】:如果您使用的是 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);
【讨论】:
仅供参考,这个实现是 O(n^2),因为 String::concat 将两个字符串复制到一个新的缓冲区:grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…【参考方案14】:Peter Lawrey 的答案的下一个变体,每个循环都没有初始化一个新字符串
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();
【讨论】:
【参考方案15】:假设仅移动指针/将字节设置为 null(或者 Java 实现 StringBuilder#setLength)更快,而不是每次通过循环检查条件以查看何时附加分隔符,您可以使用此方法:
public static String Intersperse (Collection> 集合,字符串分隔符) StringBuilder sb = new StringBuilder (); 对于(对象项目:集合) if (item == null) 继续; sb.append (item).append (分隔符); sb.setLength(sb.length() - delimiter.length()); return sb.toString();
【讨论】:
【参考方案16】:根据性能需求和要添加的元素数量,这可能是一个不错的解决方案。如果元素数量较多,Arraylist
s 重新分配内存可能会比StringBuilder
慢一些。
【讨论】:
您能否解释一下“Arraylists 重新分配内存可能比 StringBuilder 慢一点。” Arraylist 建立在标准数组之上,这些要求您设置数组的大小(如int[] a = new int[10];
)。这就是 ArrayList 正在为您做的事情,当您放入新元素时,ArrayList 必须创建一个更大的数组并将所有元素复制到新的数组中。【参考方案17】:
使用Functional 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_()), ", ");
【讨论】:
【参考方案18】:如果您的依赖项中有 json。您可以使用 new JSONArray(list).toString()
【讨论】:
【参考方案19】:在 java 8 中,您还可以使用 reducer,例如:
public static String join(List<String> strings, String joinStr)
return strings.stream().reduce("", (prev, cur) -> prev += (cur + joinStr));
【讨论】:
以上是关于连接字符串对象列表的最佳方法? [复制]的主要内容,如果未能解决你的问题,请参考以下文章