English 中文(简体)
将ArrayList转换为字符串的最佳方法
原标题:
  • 时间:2009-03-01 02:58:11
  •  标签:
This question s answers are a community effort. Edit existing answers to improve this post. It is not currently accepting new answers or interactions.

我有一个ArrayList,我想完全以字符串形式输出它。基本上,我想按照顺序输出每个元素的toString,并用制表符分隔。有什么快速的方法可以做到这一点吗?您可以循环遍历它(或删除每个元素)并将其连接到字符串,但我认为这将非常慢。

最佳回答

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 + "	";
}

System.out.println(listString);

事实上,字符串拼接将会很好,因为javac编译器将会把字符串拼接优化成StringBuilder上一系列的append操作。以下是上述程序中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 	
   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;

正如可见,编译器通过使用StringBuilder来优化循环,因此性能不应是一个大问题。

好的,在第二次查看时,每次循环都会实例化一个 StringBuilder,因此它可能不是最有效的字节码。实例化并使用明确的 StringBuilder 可能会产生更好的性能。

实际上,我认为拥有任何输出(无论是存储到磁盘还是屏幕上)都比担心字符串拼接的性能至少慢一个数量级。

编辑:正如评论中所指出的那样,上述编译器优化确实在每次迭代中创建了一个新的StringBuilder实例。(这是我之前已经注意到的。)

最优化的使用技巧是Paul Tomblin提出的响应方式,因为它只在for循环之外实例化一个单一的StringBuilder对象。

将上述代码重写为:

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("	");
}

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 	
   76:  invokevirtual   #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   79:  pop

因此,手动优化应该表现更好,因为for循环的内部较短,并且没有必要在每个迭代中实例化StringBuilder

问题回答

在Java 8或更高版本中:

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

如果list不是字符串类型,则可以使用连接收集器:

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之外没有太大用处,但我想把它添加到这个主题中...

下载Apache Commons Lang并使用该方法

 StringUtils.join(list)

 StringUtils.join(list, ", ") // 2nd param is the separator.

你当然可以自己实现,但他们的代码已经经过全面测试,可能是最好的实现方案。

我是Apache Commons库的忠实粉丝,也认为它是Java标准库的一大补充。

这是一个相当老的问题,但我认为我可以添加一个更现代化的答案 - 使用 Joiner 类来自 Guava :

String joined = Joiner.on("	").join(list);

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(", ","	")

此外,如果您的目的只是打印内容,而不太关心“#”,您可以简单地这样做:

myList.toString()

"返回类似字符串"

[str1,str2,str3]

如果您有一个数组(不是ArrayList),则您可以以以下方式实现相同的效果:

 Arrays.toString(myList).replaceAll("\[|\]", "").replaceAll(", ","	")

循环遍历并调用toString。没有什么神奇的方法,如果有,你认为它在背后做的事情除了循环遍历之外还有什么呢?唯一的微小优化可能是使用StringBuilder而不是String,但即使如此,这也不是一个巨大的胜利-连接字符串会在背后变成StringBuilder,但至少如果你以这种方式编写它,你可以看到发生了什么。

StringBuilder out = new StringBuilder();
for (Object o : list)
{
  out.append(o.toString());
  out.append("	");
}
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();
}

参数:

集合 - 要连接在一起的值的集合,可能为空。

分隔符-要使用的分隔符字符

Returns: the joined String, null if null iterator input

Since: 2.3

对于这个简单的用例,你可以简单地用逗号连接字符串。如果你使用Java 8:

String csv = String.join("	", yourArray);

否则,commons-lang 有一个 join() 方法:

String csv = org.apache.commons.lang3.StringUtils.join(yourArray, "	");

Android 有一个可以使用的 TextUtil 类:

String implode = TextUtils.join("	", list);

Java 8中很简单。请参见整数列表示例:

String result = Arrays.asList(1,2,3).stream().map(Object::toString).reduce((t, u) -> t + "	" + u).orElse("");

或者多行版本(更容易阅读的版本):

String result = Arrays.asList(1,2,3).stream()
    .map(Object::toString)
    .reduce((t, u) -> t + "	" + u)
    .orElse("");

Update - a shorter version

String result = Arrays.asList(1,2,3).stream()
                .map(Object::toString)
                .collect(Collectors.joining("	"));

一种优雅的应对尾随分隔符的方法是使用类分隔符

StringBuilder buf = new StringBuilder();
Separator sep = new Separator("	");
for (String each: list) buf.append(sep).append(each);
String s = buf.toString();

Class Separator的toString方法返回分隔符,在第一次调用时除外。因此,我们打印列表时没有尾随(或在这种情况下)前导分隔符。

It s an O(n) algorithm either way (unless you did some multi-threaded solution where you broke the list into multiple sublists, but I don t think that is what you are asking for).

只需要使用如下代码:StringBuilder

StringBuilder sb = new StringBuilder();

for (Object obj : list) {
  sb.append(obj.toString());
  sb.append("	");
}

String finalString = sb.toString();

StringBuilder 比字符串拼接要快得多,因为你不需要在每次拼接时重新实例化一个 String 对象。

In case you happen to be on Android and you are not using Jack yet (e.g. because it s still lacking support for Instant Run), and if you want more control over formatting of the resulting string (e.g. you would like to use the newline character as the divider of elements), and happen to use/want to use the StreamSupport library (for using streams on Java 7 or earlier versions of the compiler), you could use something like this (I put this method in my ListUtils class):

public static <T> String asString(List<T> list) {
    return StreamSupport.stream(list)
            .map(Object::toString)
            .collect(Collectors.joining("
"));
}

当然,确保在您的列表对象类中实现toString()函数。

In one Line : From [12,0,1,78,12] to 12 0 1 78 12

String srt= list.toString().replaceAll("\[|\]|,","");

如果您不想在最后一个元素之后加上最后一个,您必须使用索引进行检查,但请记住,只有当列表实现RandomAccess时,这才是“有效”的(即O(n))。 如果您不想在最后一个元素之后加上最后一个,您必須使用索引進行檢查,但請記住,只有當列表實現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("	");
    }
}
System.out.println(sb.toString());
List<String> stringList = getMyListOfStrings();
StringJoiner sj = new StringJoiner(" ");
stringList.stream().forEach(e -> sj.add(e));
String spaceSeparated = sj.toString()

你向new StringJoiner传递你想作为分隔符使用的字符序列。 如果您想执行CSV:new StringJoiner(", ");

以下代码可能会帮助你,

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

May not be the best way, but elegant way.

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()));
        }
    }

Output

[Test, Test2]

Would the following be any good:

List<String> streamValues = new ArrayList<>();
Arrays.deepToString(streamValues.toArray()));   

If you re using Eclipse Collections, you can use the makeString() method.

ArrayList<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");

Assert.assertEquals(
    "one	two	three",
    ArrayListAdapter.adapt(list).makeString("	"));

If you can convert your ArrayList to a FastList, you can get rid of the adapter.

Assert.assertEquals(
    "one	two	three",
    FastList.newListWith("one", "two", "three").makeString("	"));

Note: I am a committer for Eclipse Collections.

For seperating using tabs instead of using println you can use 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("	");
}

I see quite a few examples which depend on additional resources, but it seems like this would be the simplest solution: (which is what I used in my own project) which is basically just converting from an ArrayList to an Array and then to a List.

    List<Account> accounts = new ArrayList<>();

   public String accountList() 
   {
      Account[] listingArray = accounts.toArray(new Account[accounts.size()]);
      String listingString = Arrays.toString(listingArray);
      return listingString;
   }

This is quite an old conversation by now and apache commons are now using a StringBuilder internally: http://commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line.3045

This will as we know improve performance, but if performance is critical then the method used might be somewhat inefficient. Whereas the interface is flexible and will allow for consistent behaviour across different Collection types it is somewhat inefficient for Lists, which is the type of Collection in the original question.

I base this in that we are incurring some overhead which we would avoid by simply iterating through the elements in a traditional for loop. Instead there are some additional things happening behind the scenes checking for concurrent modifications, method calls etc. The enhanced for loop will on the other hand result in the same overhead since the iterator is used on the Iterable object (the List).

You can use a Regex for this. This is as concise as it gets

System.out.println(yourArrayList.toString().replaceAll("\[|\]|[,][ ]","	"));

How about this function:

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();
}

it works for any type of collection...





相关问题
热门标签