Preferred Idiom for Joining a Collection of Strings in Java
给定一个字符串集合,在没有使用外部库的情况下,如何将它们加入到普通Java中?
考虑到这些变量:
1 2 3
| Collection <String > data = Arrays. asList("Snap", "Crackle", "Pop");
String separator =",";
String joined ; // let's create this, shall we? |
这就是我在番石榴的方式:
1
| joined = Joiner.on(separator).join(data); |
在Apache Commons/lang中:
1
| joined = StringUtils.join(data, separator); |
但在普通Java中,真的没有比这更好的方法吗?
1 2 3 4 5 6
| StringBuilder sb = new StringBuilder ();
for(String item : data ){
if(sb. length()>0)sb. append(separator );
sb. append(item );
}
joined = sb. toString(); |
- 请参阅stackoverflow.com/questions/3426995/&hellip;、stackoverflow.com/questions/1751844/&hellip;、stackoverflow.com/questions/1515437/&hellip;
- @S&233;Bastien这些问题都没有比我短的答案,也没有使用外部库。
- stackoverflow.com/question s/1751844/&hellip;上接受的答案确实回答了您的问题:不,除了第三方库之外,没有更好的方法。
- 事实上,没有简单、干净的方法可以做到这一点,这就解释了为什么必须将它添加到所有这些库中。
- @大家都知道,凯文——)
- 添加了Java 8的方式来回答我的问题。
我想说,最好的方法是在不使用番石榴的情况下使用番石榴内部使用的技术(最好你不是指"最简洁"),在你的例子中,这种方法看起来像这样:
1 2 3 4 5 6 7 8 9
| Iterator <String > iter = data. iterator();
StringBuilder sb = new StringBuilder ();
if (iter. hasNext()) {
sb. append(iter. next());
while (iter. hasNext()) {
sb. append(separator ). append(iter. next());
}
}
String joined = sb. toString(); |
这不需要在迭代时进行布尔检查,也不需要对字符串进行任何后处理。
可能不是在循环中一次又一次地调用sb.length(),我做了一些修改。
1 2 3 4 5 6 7 8
| StringBuilder sb = new StringBuilder ();
String separator ="";
for(String item : data ){
sb. append(separator );
separator =",";
sb. append(item );
}
joined = sb. toString(); |
- 这是原版,谢谢(+1)
- 为什么编辑?旧的基于字符串的版本更加灵活,新的版本在开头添加了不必要的空间。
- 抱歉,过早优化:)
- 我也是这样做的。它避免了额外的if,而支持额外的无条件语句,这使得它在Bob Martin的"转换优先级前提"(blog.8thlight.com/uncle bob/2013/05/27/&hellip;)框架中更可取。执行速度也可能更快(分支在处理器中会有性能损失,而重复分配的成本可能很低;循环展开器也可能更容易优化掉这种形式的额外语句)。
Java 8 +
joined =String.join(separator, data);号
Java 7及以下
1 2 3 4 5 6 7
| StringBuilder sb = new StringBuilder ();
boolean first = true;
for(String item : data ){
if(!first || (first = false)) sb. append(separator );
sb. append(item );
}
joined = sb. toString(); |
- 我不喜欢使用if(first),因为它不必要地冗长,但我必须同意if(!first || (first = false))是漂亮的(+1)
- 我本来打算给@suresh s打勾,但我不喜欢当前的编辑,下面是
- 而不是你的第一个例子,你可以这样做:joined = data.toString().substring(1, joined.length() - 2);
- @但是,在将有意义的数据分配给joined之前,您不能使用joined.length()。
- 很好,@aioobe-我去拿外套!;-)虽然我注意到自从我发表评论以来,你已经完全改变了你的答案,所以不管怎样,它不再有意义-如果它在第一时间是正确的,而不是……
在Java 8中:
收集器可以加入流,如果您希望filter或map您的数据,这非常有用。
1 2 3
| String joined = Stream. of("Snap", "Crackle", "Pop")
. map(String::toUpperCase )
. collect(Collectors. joining(",")); |
要从集合中获取流,请在集合上调用stream():
1
| Arrays. asList("Snap", "Crackle", "Pop"). stream() |
不同的意图:
对于第三部分的工具,如guava的Joiner,它不是很快,但非常灵活。有许多自定义连接行为的选项方法,例如skipNulls()。
普通Java是快速的,但它需要你自己编写一些代码。
- 我认为Joiner速度非常快,特别是如果您将预构建的Joiner重新用于特定的分隔符。它使用一个StringBuilder来构建,并且它有一个非常有趣的方法来确保它只在正确的地方添加一个分隔符,而不需要每次迭代都进行布尔检查。
你的建议是一个可能的解决方案,另一个常见的建议是:
1 2 3 4 5
| for (String item : data ) {
sb. append(item )
sb. append(separator );
}
String joined = sb. substring(0, sb. length() - separator. length()) |
- 更像是:sb.substring(0, sb.length() - separator.length())。但我会称之为一个可怕的黑客
- 它是。事实上,您必须在黑客(通常是无用的测试)和使用迭代器提取列表的第一个成员之间进行选择。没有完美的解决方案。