我有一个ArrayList
我想完全输出为字符串。本质上,我想使用toString
由制表符分隔的每个元素按顺序输出它。有什么快速的方法可以做到这一点吗?您可以遍历它(或删除每个元素)并将其连接到一个字符串,但我认为这会很慢。
27 回答
在 Java 8 或更高版本中:
String listString = String.join(", ", list);
如果list
is 不是 String 类型,则可以使用连接收集器:
String listString = list.stream().map(Object::toString)
.collect(Collectors.joining(", "));
如果您碰巧在 Android 上执行此操作,则有一个很好的实用程序,称为TextUtils,它有一个.join(String delimiter, Iterable)
方法。
List<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
String joined = TextUtils.join(", ", list);
显然在Android之外没有太多用处,但我想我会把它添加到这个线程中......
Java 8 引入了一个String.join(separator, list)
方法;请参阅Vitalii Federenko 的回答。
在 Java 8 之前,使用循环迭代ArrayList
是唯一的选择:
不要使用此代码,请继续阅读此答案的底部以了解为什么不希望使用它,以及应该使用哪个代码:
ArrayList<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
String listString = "";
for (String s : list)
{
listString += s + "\t";
}
System.out.println(listString);
事实上,字符串连接会很好,因为javac
编译器会将字符串连接优化为对 a 的一系列append
操作StringBuilder
。for
这是从上述程序的循环中反汇编字节码的一部分:
61: new #13; //class java/lang/StringBuilder
64: dup
65: invokespecial #14; //Method java/lang/StringBuilder."<init>":()V
68: aload_2
69: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
72: aload 4
74: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
77: ldc #16; //String \t
79: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
82: invokevirtual #17; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
可以看出,编译器通过使用 a 优化了该循环StringBuilder
,因此性能应该不是一个大问题。
(好吧,乍一看,StringBuilder
循环的每次迭代都会实例化 ,因此它可能不是最有效的字节码。实例化和使用显式StringBuilder
可能会产生更好的性能。)
事实上,我认为拥有任何类型的输出(无论是到磁盘还是到屏幕)都至少比担心字符串连接的性能慢一个数量级。
编辑:正如评论中所指出的,上述编译器优化确实是StringBuilder
在每次迭代时创建一个新实例。(我之前已经注意到了。)
使用的最优化技术将是Paul Tomblin的响应,因为它仅在循环StringBuilder
外实例化单个对象。for
将上述代码重写为:
ArrayList<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
StringBuilder sb = new StringBuilder();
for (String s : list)
{
sb.append(s);
sb.append("\t");
}
System.out.println(sb.toString());
只会StringBuilder
在循环外实例化一次,并且只会对循环内的方法进行两次调用append
,如以下字节码所示(它显示了StringBuilder
循环的实例化):
// Instantiation of the StringBuilder outside loop:
33: new #8; //class java/lang/StringBuilder
36: dup
37: invokespecial #9; //Method java/lang/StringBuilder."<init>":()V
40: astore_2
// [snip a few lines for initializing the loop]
// Loading the StringBuilder inside the loop, then append:
66: aload_2
67: aload 4
69: invokevirtual #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
72: pop
73: aload_2
74: ldc #15; //String \t
76: invokevirtual #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
79: pop
因此,确实手动优化应该表现得更好,因为for
循环的内部更短,并且不需要StringBuilder
在每次迭代时实例化 a。
下载 Apache Commons Lang 并使用方法
StringUtils.join(list)
StringUtils.join(list, ", ") // 2nd param is the separator.
当然,您可以自己实现它,但他们的代码已经过全面测试,可能是最好的实现。
我是 Apache Commons 库的忠实粉丝,我也认为它是对 Java 标准库的一个很好的补充。
将List更改为可读且有意义的String确实是每个人都可能遇到的常见问题。
案例1。如果您的类路径中有 apache 的 StringUtils(来自 rogerdpack 和 Ravi Wallau):
import org.apache.commons.lang3.StringUtils;
String str = StringUtils.join(myList);
案例 2。如果您只想使用 JDK(7) 中的方法:
import java.util.Arrays;
String str = Arrays.toString(myList.toArray());
永远不要自己造轮子,不要使用循环来完成这一单行任务。
如果您正在寻找一种快速的单线,从 Java 5 开始,您可以这样做:
myList.toString().replaceAll("\\[|\\]", "").replaceAll(", ","\t")
此外,如果您的目的只是打印内容并且不太关心“\t”,您可以简单地执行以下操作:
myList.toString()
它返回一个字符串,如
[str1,str2,str3]
如果你有一个数组(不是 ArrayList),那么你可以像这样完成同样的事情:
Arrays.toString(myList).replaceAll("\\[|\\]", "").replaceAll(", ","\t")
循环遍历它并调用 toString。没有什么神奇的方法,如果有的话,你认为除了循环遍历它之外,它会在幕后做什么?唯一的微优化是使用 StringBuilder 而不是 String,即使这也不是一个巨大的胜利 - 连接字符串在幕后变成了 StringBuilder,但至少如果你这样写,你可以看到发生了什么。
StringBuilder out = new StringBuilder();
for (Object o : list)
{
out.append(o.toString());
out.append("\t");
}
return out.toString();
大多数 Java 项目通常都有可用的 apache-commons lang。StringUtils.join() 方法非常好,并且有多种风格可以满足几乎所有需求。
public static java.lang.String join(java.util.Collection collection,
char separator)
public static String join(Iterator iterator, String separator) {
// handle null, zero and one elements before building a buffer
Object first = iterator.next();
if (!iterator.hasNext()) {
return ObjectUtils.toString(first);
}
// two or more elements
StringBuffer buf =
new StringBuffer(256); // Java default is 16, probably too small
if (first != null) {
buf.append(first);
}
while (iterator.hasNext()) {
if (separator != null) {
buf.append(separator);
}
Object obj = iterator.next();
if (obj != null) {
buf.append(obj);
}
}
return buf.toString();
}
参数:
collection - 要连接在一起的值的集合,可能为 null
separator - 要使用的分隔符
返回:连接的字符串,如果为空迭代器输入则为空
自:2.3
Android 有一个 TextUtil 类,您可以使用http://developer.android.com/reference/android/text/TextUtils.html
String implode = TextUtils.join("\t", list);
对于这个简单的用例,您可以简单地用逗号连接字符串。如果您使用 Java 8:
String csv = String.join("\t", yourArray);
否则 commons-lang 有一个 join() 方法:
String csv = org.apache.commons.lang3.StringUtils.join(yourArray, "\t");
处理尾随分隔符的一种优雅方法是使用Class Separator
StringBuilder buf = new StringBuilder();
Separator sep = new Separator("\t");
for (String each: list) buf.append(sep).append(each);
String s = buf.toString();
Class Separator 的 toString 方法返回分隔符,但第一次调用除外。因此,我们打印没有尾随(或在这种情况下)前导分隔符的列表。
在 Java 8 中,这很简单。有关整数列表,请参见示例:
String result = Arrays.asList(1,2,3).stream().map(Object::toString).reduce((t, u) -> t + "\t" + u).orElse("");
或多行版本(更易于阅读):
String result = Arrays.asList(1,2,3).stream()
.map(Object::toString)
.reduce((t, u) -> t + "\t" + u)
.orElse("");
更新 - 更短的版本
String result = Arrays.asList(1,2,3).stream()
.map(Object::toString)
.collect(Collectors.joining("\t"));
无论哪种方式,它都是一种O(n)
算法(除非您做了一些多线程解决方案,将列表分成多个子列表,但我认为这不是您所要求的)。
只需使用StringBuilder
如下:
StringBuilder sb = new StringBuilder();
for (Object obj : list) {
sb.append(obj.toString());
sb.append("\t");
}
String finalString = sb.toString();
这StringBuilder
将比字符串连接快得多,因为您不会String
在每个连接上重新实例化一个对象。
如果您碰巧在 Android 上并且您还没有使用 Jack(例如,因为它仍然缺乏对 Instant Run 的支持),并且如果您想要更多地控制结果字符串的格式(例如,您想使用换行符作为元素的分隔符),并且碰巧使用/想要使用 StreamSupport 库(用于在 Java 7 或更早版本的编译器上使用流),你可以使用这样的东西(我把这个方法放在我的 ListUtils 类中):
public static <T> String asString(List<T> list) {
return StreamSupport.stream(list)
.map(Object::toString)
.collect(Collectors.joining("\n"));
}
当然,请确保在列表对象的类上实现 toString()。
在一行中:从 [12,0,1,78,12] 到 12 0 1 78 12
String srt= list.toString().replaceAll("\\[|\\]|,","");
如果您不想在最后一个元素之后出现最后一个 \t,则必须使用索引来检查,但请记住,这仅在列表实现 RandomAccess 时“有效”(即 O(n))。
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
StringBuilder sb = new StringBuilder(list.size() * apprAvg); // every apprAvg > 1 is better than none
for (int i = 0; i < list.size(); i++) {
sb.append(list.get(i));
if (i < list.size() - 1) {
sb.append("\t");
}
}
System.out.println(sb.toString());
如果您使用的是Eclipse Collections,则可以使用该makeString()
方法。
ArrayList<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
Assert.assertEquals(
"one\ttwo\tthree",
ArrayListAdapter.adapt(list).makeString("\t"));
如果您可以将您的转换ArrayList
为 a FastList
,则可以摆脱适配器。
Assert.assertEquals(
"one\ttwo\tthree",
FastList.newListWith("one", "two", "three").makeString("\t"));
注意:我是 Eclipse Collections 的提交者。
下面的代码可以帮助你,
List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
String str = list.toString();
System.out.println("Step-1 : " + str);
str = str.replaceAll("[\\[\\]]", "");
System.out.println("Step-2 : " + str);
输出:
Step-1 : [1, 2, 3]
Step-2 : 1, 2, 3
可能不是最好的方式,但优雅的方式。
Arrays.deepToString(Arrays.asList("Test", "Test2")
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
System.out.println(Arrays.deepToString(Arrays.asList("Test", "Test2").toArray()));
}
}
输出
[测试,测试2]
以下内容会有什么好处:
List<String> streamValues = new ArrayList<>();
Arrays.deepToString(streamValues.toArray()));
List<String> stringList = getMyListOfStrings();
StringJoiner sj = new StringJoiner(" ");
stringList.stream().forEach(e -> sj.add(e));
String spaceSeparated = sj.toString()
您传递给new StringJoiner
要用作分隔符的字符序列。如果你想做一个 CSV:new StringJoiner(", ");
要使用制表符而不是使用println进行分隔,您可以使用print
ArrayList<String> mylist = new ArrayList<String>();
mylist.add("C Programming");
mylist.add("Java");
mylist.add("C++");
mylist.add("Perl");
mylist.add("Python");
for (String each : mylist)
{
System.out.print(each);
System.out.print("\t");
}
我看到了很多依赖于额外资源的示例,但这似乎是最简单的解决方案:(这是我在自己的项目中使用的)基本上只是从 ArrayList 转换为 Array 然后再转换为 List .
List<Account> accounts = new ArrayList<>();
public String accountList()
{
Account[] listingArray = accounts.toArray(new Account[accounts.size()]);
String listingString = Arrays.toString(listingArray);
return listingString;
}
到目前为止,这是一个相当古老的对话,apache commons 现在在内部使用 StringBuilder: http: //commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line。 3045
正如我们所知,这将提高性能,但如果性能很关键,那么使用的方法可能效率低下。虽然接口很灵活,并且允许在不同的 Collection 类型之间保持一致的行为,但对于 Lists 来说效率有点低,这是原始问题中的 Collection 类型。
我之所以这样做是因为我们会产生一些开销,我们可以通过简单地遍历传统 for 循环中的元素来避免这些开销。相反,在检查并发修改、方法调用等的幕后会发生一些额外的事情。另一方面,增强的 for 循环将导致相同的开销,因为迭代器用于 Iterable 对象(列表)。
您可以为此使用正则表达式。这是最简洁的
System.out.println(yourArrayList.toString().replaceAll("\\[|\\]|[,][ ]","\t"));
这个功能怎么样:
public static String toString(final Collection<?> collection) {
final StringBuilder sb = new StringBuilder("{");
boolean isFirst = true;
for (final Object object : collection) {
if (!isFirst)
sb.append(',');
else
isFirst = false;
sb.append(object);
}
sb.append('}');
return sb.toString();
}
它适用于任何类型的收藏...