【问题标题】:Best way to convert an ArrayList to a string将 ArrayList 转换为字符串的最佳方法
【发布时间】:2010-10-10 14:15:29
【问题描述】:

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

【问题讨论】:

  • 或删除每个元素请注意,从 ArrayList 列表中删除元素是一个很大的禁忌,因为您的“循环”将因元素移动而花费二次时间(提供你从第一个元素循环到最后一个元素)。

标签: java string arraylist


【解决方案1】:

在 Java 8 或更高版本中:

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

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

String listString = list.stream().map(Object::toString)
                        .collect(Collectors.joining(", "));

【讨论】:

  • 我不敢相信直到 Java 8 才添加了这个。
  • 这个答案应该得到更多的投票!没有 hack,没有库,也没有循环。
  • 这不适用于 OP 所说的。 String.join 作为第二个参数 Iterable。因此,如果您有一个要显示的 List 则它​​可以工作,但如果您有 List 它会 not 按照 OP 的要求在其中调用 toString()
  • 为什么这不是第一个(并且被接受的)答案..我总是必须向下滚动直到我知道它在那里..优雅而简洁
  • 需要 Android API 26+
【解决方案2】:

如果您碰巧在 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 之外没有太多用处,但我想我会把它添加到这个线程中......

【讨论】:

  • 由于TextUtils.join 采用Object[],我假设它在每个令牌上调用toString()PatientDetails 是否实现 toString()?如果这不能解决问题,你可能会被 Separator 类卡住。
  • org.apache.lang3.StringUtils 的作用几乎相同,所以你的回答对我来说在 Android 之外绝对有用:)
  • 这个答案的受欢迎程度(目前是票数最高的一个)告诉你很多java问题源于Android开发
  • 我敢打赌你被派到这个地球来解决我的问题:)
  • 你救了我的命。
【解决方案3】:

Java 8 引入了String.join(separator, list) 方法;见Vitalii Federenko's answer

在 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 编译器会将字符串连接优化为对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 \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;

可以看出,编译器通过使用StringBuilder 优化该循环,因此性能应该不是大问题。

(好吧,乍一看,StringBuilder 在循环的每次迭代中都会被实例化,因此它可能不是最有效的字节码。实例化并使用显式的StringBuilder 可能会产生更好的性能。)

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

编辑: 正如 cmets 中所指出的,上述编译器优化确实是在每次迭代时创建 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("\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

【讨论】:

  • 出于性能原因考虑使用 StringBuilder 类而不是仅仅附加字符串。
  • 编译器将优化字符串连接,但每次执行循环时都会创建一个新的 StringBuilder 对象。
  • -1 用于 +=,在这种情况下它是性能杀手。始终使用 StringBuilder 重复附加到字符串。
  • 此解决方案在字符串末尾添加了一个额外的“\t”,这通常是不受欢迎的,尤其是当您想要创建逗号分隔列表或类似列表时。我认为这里发布的其他解决方案(使用 Apache Commons 或 Guava)更胜一筹。
  • 这不是最佳答案。在下面查看Java 的 Vitalii 或 Android 的 Geewax
【解决方案4】:

下载Apache Commons Lang并使用方法

 StringUtils.join(list)

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

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

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

【讨论】:

  • 不幸的是,SO 的居民更愿意重新发明轮子。
  • 也在 Apache Commons Lang 中。用法看起来像StringUtils.join(list.toArray(),"\t")
  • 这个特殊的轮子几乎不值得额外依赖。
  • @SevaAlekseyev 我认为这值得商榷。如果您立即添加此依赖项,您将更频繁地使用它的类。而不是使用 "if string != null && string.trim() != "" 您将使用 StringUtils.isNotEmpty... 您将使用 ObjectUtils.equals() 这样您就不必到处检查 null。但是如果您等待证明使用这些库的唯一依赖项,您可能永远不会真正使用它们。
  • 最后不加标签也不错!
【解决方案5】:

这是一个相当古老的问题,但我想我不妨添加一个更现代的答案 - 使用来自 GuavaJoiner 类:

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

【讨论】:

  • 这是我最喜欢的解决方案。 :-> 番石榴 +1。
  • Ctrl+F 也适用于 Guava。谢谢!
  • List list = new ArrayList(); list.add("你好"); list.add("嘿"); list.add("嗨"); System.out.println(list.toString().substring(1, list.toString().length()-1).replaceAll(",", "\t"));
  • Guava Joiner 的美妙之处在于它还可以使用Joiner.on(", ").withKeyValueSeparator(": ").join(myMap) 加入地图
【解决方案6】:

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

永远不要自己造轮子,不要为这个单行任务使用循环。

【讨论】:

  • 在发布我自己的解决方案之前,我没有注意到您的案例 2 解决方案。您的实际上要好得多,并且节省了额外的步骤。我肯定会推荐它而不是一些似乎需要额外工具的答案。
  • Arrays.toString(myList.toArray()) - 最简单最直接的解决方案
  • 我认为这应该被认为是最好和最有效的解决方案
  • Arrays.toString(list.toArray()) 好写,但效率极低。
【解决方案7】:

如果您正在寻找一种快速的单线,从 Java 5 开始,您可以这样做:

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

另外,如果您的目的只是打印出内容并且不太关心“\t”,您可以简单地这样做:

myList.toString()

返回一个类似的字符串

[str1,str2,str3]

如果你有一个数组(不是 ArrayList),那么你可以像这样完成同样的事情:

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

【讨论】:

  • 我认为这实际上比上面发布的方法更有效。太可惜了,它显示在底部。它可能并不优雅,但我坚信您的解决方案运行 O(n),而其他解决方案理论上运行 O(n^2)(因为 Java 字符串是不可变的,您基本上在每次合并时创建一个新字符串,并且随着结果字符串变大)
  • 如果你的文本非常大,你最终会吃掉你的计算机资源。
  • 这种方法比使用 StringBuilder 慢 7-8 倍。
  • @user3790827 你在看一个非常肤浅的层面。如果我告诉你在x 州的n 城市的m 家庭中搜索一个人,你会说它是O(mnx)O(n^3),但我可以改写为“在这个国家,”你会立即告诉我O(n)。最重要的是,这里所有的算法一般都是O(n),没有循环within循环。
【解决方案8】:

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

StringBuilder out = new StringBuilder();
for (Object o : list)
{
  out.append(o.toString());
  out.append("\t");
}
return out.toString();

【讨论】:

  • 谢谢!这就是我最终要做的:)
  • 对于大型阵列,这绝对不是微优化。使用 StringBuilder 的自动转换是针对每个字符串连接单独完成的,这在循环的情况下没有帮助。如果在一个表达式中连接大量元素是可以的。
  • 如果您要连接 50 000 个字符串,生成约 1 MB 结果字符串,那么显式使用 StringBuilder 是一件大事 - 与 1 秒的执行时间相比,它可能相差 1 分钟。
【解决方案9】:

大多数 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 - 要连接在一起的值的 Collection,可能为 null

分隔符 - 要使用的分隔符

返回:加入的String,如果是null 空迭代器输入

因为: 2.3

【讨论】:

  • 这非常好,因为join(...) 方法具有数组和集合的变体。
  • 我需要创建包含很少填充元素和随机数的字符串集合。最后我需要一个字符串,而不是数组而不是数组到字符串。这是我的代码: Collections.shuffle(L); StringBuilder sb = new StringBuilder(); L.forEach(e -> sb.append(e)); return sb.toString();
【解决方案10】:

Android 有一个 TextUtil 类你可以使用http://developer.android.com/reference/android/text/TextUtils.html

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

【讨论】:

  • 这个方案优于String.join,需要API 26+
【解决方案11】:

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

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

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

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

【讨论】:

    【解决方案12】:

    处理尾随分隔符的一种优雅方法是使用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 方法返回分隔符,except 用于第一次调用。因此,我们打印的列表没有尾随(或在这种情况下)前导分隔符。

    【讨论】:

      【解决方案13】:

      在 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"));
      

      【讨论】:

      • 这可能只是一行代码,但在我看来并不简单。对我来说,遍历 List 并将其元素解析为 String 比这更简单。
      • 这也是我的第一印象。但是习惯了之后,我觉得它很棒。例如,您可以添加filter 之类的操作。最后,我更容易阅读代码。
      • 在 Java 8 中:String.join("\t", array)
      • 只有当内容也是String 时,最后一条评论才有效。如果它是Integers 的列表,你有问题
      • 所有这 3 个都需要 android 上的 API 级别 24。
      【解决方案14】:

      无论哪种方式,它都是O(n) 算法(除非您做了一些多线程解决方案,将列表分成多个子列表,但我认为这不是您所要求的)。

      只需使用StringBuilder,如下所示:

      StringBuilder sb = new StringBuilder();
      
      for (Object obj : list) {
        sb.append(obj.toString());
        sb.append("\t");
      }
      
      String finalString = sb.toString();
      

      StringBuilder 将比字符串连接快得多,因为您不会在每次连接时重新实例化 String 对象。

      【讨论】:

        【解决方案15】:

        如果您碰巧在 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()。

        【讨论】:

        • 使用reduce 对字符串连接效率极低。您应该使用 Java 8 方式 return StreamSupport.stream(list).map(Object::toString).collect(joining("\n")),它在内部使用 StringJoiner。你也没有理由不能用streamsupport 做到这一点。
        • 另一件事是,如果通过一个空列表,您的代码将严重失败(由于Optional#get)。
        • @StefanZobel 感谢您指出效率和边缘情况问题。修改了代码。
        【解决方案16】:

        在一行中: 从 [12,0,1,78,12] 到 12 0 1 78 12

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

        【讨论】:

        • 1) 没有理由再使用这样的代码了(截至 4 年前!)。 2)此答案不会在问答中添加任何内容。
        • 这非常有效。对于逗号分隔的字符串,请使用: String srt= list.toString().replaceAll("\[|\]","");
        【解决方案17】:

        如果您不希望最后一个元素之后的最后一个 \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());
        

        【讨论】:

          【解决方案18】:

          下面的代码可能会对你有所帮助,

          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
          

          【讨论】:

            【解决方案19】:

            可能不是最好的方式,但优雅的方式。

            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]

            【讨论】:

            • 如果“最好”意味着代码可读性,我认为这是最好的方法。我没有看过代码,但它可能与 StringBuilder 方法一样高效(它可能在后台使用 StringBuilder),尽管在输出中不能自定义。
            【解决方案20】:

            以下是否有好处:

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

            【讨论】:

              【解决方案21】:

              如果您使用的是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 转换为FastList,则可以摆脱适配器。

              Assert.assertEquals(
                  "one\ttwo\tthree",
                  FastList.newListWith("one", "two", "three").makeString("\t"));
              

              注意:我是 Eclipse Collections 的提交者。

              【讨论】:

                【解决方案22】:
                List<String> stringList = getMyListOfStrings();
                StringJoiner sj = new StringJoiner(" ");
                stringList.stream().forEach(e -> sj.add(e));
                String spaceSeparated = sj.toString()
                

                您将要用作分隔符的字符序列传递给new StringJoiner。如果你想做一个 CSV:new StringJoiner(", ");

                【讨论】:

                  【解决方案23】:

                  要使用标签而不是使用 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");
                  }
                  

                  【讨论】:

                    【解决方案24】:

                    我看到了很多依赖于额外资源的示例,但这似乎是最简单的解决方案:(这是我在自己的项目中使用的)基本上只是从 ArrayList 转换为 Array 然后到一个列表。

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

                    【讨论】:

                      【解决方案25】:

                      到目前为止,这是一个相当古老的对话,apache commons 现在在内部使用 StringBuilder:http://commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line.3045

                      据我们所知,这将提高性能,但如果性能很关键,那么所使用的方法可能会有些低效。虽然接口很灵活,并且允许跨不同集合类型的一致行为,但对于列表来说效率有点低,这是原始问题中的集合类型。

                      我之所以这样做是因为我们会产生一些开销,我们可以通过简单地遍历传统 for 循环中的元素来避免这些开销。相反,在检查并发修改、方法调用等的幕后会发生一些额外的事情。另一方面,增强的 for 循环将导致相同的开销,因为迭代器用于 Iterable 对象(列表)。

                      【讨论】:

                        【解决方案26】:

                        您可以为此使用正则表达式。这是最简洁的

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

                        【讨论】:

                          【解决方案27】:

                          这个功能怎么样:

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

                          它适用于任何类型的集合...

                          【讨论】:

                            猜你喜欢
                            • 2017-11-08
                            • 1970-01-01
                            • 2019-10-24
                            • 2014-06-21
                            • 2011-12-27
                            • 2017-07-26
                            • 1970-01-01
                            • 2010-10-22
                            相关资源
                            最近更新 更多