【问题标题】:How to get String values from ArrayList and store them in a single string separated by commas, in Java 8?如何从 ArrayList 获取字符串值并将它们存储在一个用逗号分隔的字符串中,在 Java 8 中?
【发布时间】:2015-10-08 17:58:25
【问题描述】:

我有一个带有一些字符串的 ArrayList。我想将 ArrayList 中的数字列表存储在一个用逗号分隔的字符串中,如下所示。

String s = "350000000000050287,392156486833253181,350000000000060764"

这是我的清单:

   List<String> e = new ArrayList<String>();

    e.add("350000000000050287");
    e.add("392156486833253181");
    e.add("350000000000060764");

我一直在尝试以下方式:

     StringBuilder s = new StringBuilder();
    for (String id : e){

        s.append(id+",");

    }

唯一的问题是这会在末尾添加一个逗号,我不希望这样做。最好的方法是什么?

谢谢

【问题讨论】:

  • Google Guava 的木匠
  • 另见stackoverflow.com/questions/205555/…——他们的答案主要集中在适用于 Java 8 之前的解决方案。这个问题要么是重复的,要么可能集中在 Java 8 及以下。
  • @SteveKuo Java 8 偷了那个 ;)
  • @AndyThomas 什么?等待。您如何得出 OP 要求 Java 8 解决方案的结论?
  • 通过删除最后一个逗号? :) delete/deleteCharAt/setLength

标签: java list java-8 stringbuilder


【解决方案1】:

最简单的解决方案是使用String.join

List<String> list = new ArrayList<String>();

list.add("11");
list.add("22");
list.add("33");

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

System.out.println(joined);
//prints "11,22,33"

请注意,这需要 Java 8。


但是,如果您想支持旧版本的 Java,您可以使用迭代器修复您的代码:

StringBuilder sb = new StringBuilder();

Iterator<String> iterator = list.iterator();

// First time (no delimiter):
if (iterator.hasNext()) {
    sb.append(iterator.next());

    // Other times (with delimiter):
    while (iterator.hasNext()) {
        sb.append(",");
        sb.append(iterator.next());
    }
}

或者简单地使用布尔值来确定第一次:

StringBuilder sb = new StringBuilder();

boolean firstTime = true;

for (String str : list) {

    if (firstTime) {
        firstTime = false;
    } else {
        sb.append(",");
    }

    sb.append(str);
}

但显然后者的性能应该低于使用迭代器比较每个方法生成的字节码。但是,正如Tagir Valeev 指出的那样,这可能不是真的:benchmark 向我们展示了使用标志在从 10 次开始的迭代次数中性能更高。

如果有人能解释为什么会这样,我很高兴知道。

【讨论】:

  • 如果您没有 CharSequence 或其子类之一的列表,您可以获取一个流,将每个对象映射到它的 toString 表示并像这样使用 collect(joining(","))String s = myList.stream().map(Object::toString).collect(Collectors.joining(","));
  • "但后者的性能显然不如使用迭代器。" - 为什么?这绝对不明显。你测量过性能吗?
  • @AlexisC.,实际上我不知道他们为什么犹豫要在加入收集器中添加自动 Object.toString()String.valueOf。在我的库中我做了这一步,所以你可以使用StreamEx.of(myList).joining(",")
  • 这是benchmark (8u60, x64)。标志版本仅对单个元素较慢,然后变得更快。 String.join 是预期中最慢的(它将在 Java-9 中进行优化)。 Holger 的setLength() 解决方案比flag 解决方案慢得惊人...
  • @TagirValeev 为什么:因为所谓的enhanced for statementwill be compiled using iterators。这将产生完全相同的代码,除了额外的分支。 sbFlag 在您和我的计算机上的性能优于 sbFirst 的原因可能是编译器优化或测试设置方式。我真的说不出来。但是您可能想查看为这些方法生成的字节码,以了解我的意思。
【解决方案2】:

为那个 Java 8 解决方案投票支持 Tim ;)

如果你没有使用 JDK 8

   StringBuilder s = new StringBuilder();
    for (String id : e){

        s.append(id).append(",");

    }
   String result =s.toString().replaceAll(",$", "");

我使用的正则表达式",$" 是检测最后一个逗号。

如果你看到了,我将 s.append(id +","); 替换为 s.append(id).append(","); 以获得更好的性能

【讨论】:

  • 我打算添加“......并向上帝祈祷你的最后一项不会以逗号结尾”,但后来我意识到这不会影响任何事情。有没有这种方法行不通的情况?
  • @dberm22 它将始终匹配最后一个 ,,因为 $ 表示字符串的结尾。但是,对我来说,在最后一个索引处搜索, 似乎是多余的,而我们都知道如果e.length() &gt; 0 肯定包含,。所以String.substring 会更合适(我认为)。
  • @Tim:不,最有效的解决方案是在调用s.toString() 之前 执行if(s.length()&gt;0) s.setLength(s.length()-1);,因此您不需要substring 操作。
  • @Holger 你是绝对正确的,这是一种更有效的方法。但是,我认为,最高效的解决方案是使用迭代器,而不是首先附加最后一个 ","
【解决方案3】:

您可以使用布尔标志来检查第一次迭代,如果不是第一次迭代,则在追加 id 之前追加 ","

这可能会解决您的问题。

boolean first = true;
for (String id : e){
    if(!first)
        s.append(",");
    else
        first = false;
    s.append(id);

}

注意

如果你使用Java8,那么按照Tim的解决方案。

【讨论】:

  • 这将在 OP 可能关心性能的每次迭代中添加额外的条件检查,但在循环完成后添加逗号并删除它会快得多。
  • @draksia 与其他操作(例如:s.append(id))相比,使用额外的条件检查导致的性能损失可以忽略不计。
  • 由于整个操作可能只有在我们谈论大量迭代时才与性能相关,所以您不应低估条件的成本。但是,您可以期望 Hotspot 足够聪明,可以在这种特定情况下预测条件的结果并将其完全删除。因此,这只是一个品味问题。我个人不喜欢让循环体更复杂,更喜欢简单的循环并删除一个额外的,。但这只是我。
  • @Holger,即使 HotSpot 未能删除此检查,CPU 分支预测器也会在此处多次迭代后正常工作。我期望从创建堆对象、重新分配和复制StringBuilder 内部缓冲区中获得更多开销。
  • @Holger,我不会说重新分配/复制已被优化掉。我不知道这方面的证据。目前只优化了一些简单的非常具体的 StringBuilder 模式(如new StringBuilder().append(str).append(intNum).toString() 可以转换为直接预分配目标char[] 缓冲区并在其上创建一个字符串)。我根本没有批评您的解决方案,这很好(尽管我个人更喜欢first 标志),我只是说与整个循环操作相比,额外可预测分支的成本可以忽略不计,因此性能在这里并不重要, 只有样式。
【解决方案4】:
StringBuilder s = new StringBuilder();
for(int i = 0; i < e.size()-1; i++){
    s.append(e.get(i)+",")
}
s.append(e.get(i))
【解决方案5】:

Collectors.joining(","),在您的情况下运行良好,示例实现:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class Solution {
    public static void main(String[] args) {
        List<String> e = new ArrayList<>();
        e.add("350000000000050287");
        e.add("392156486833253181");
        e.add("350000000000060764");
        System.out.println(e.stream().collect(Collectors.joining(",")));
    }
}

输出:

350000000000050287,392156486833253181,350000000000060764

【讨论】:

    【解决方案6】:

    尝试通过检查当前索引是否是列表的最后一个值(e.size-1)来遍历您的列表,如果它不是最后一个值,则将字符串与正常的“,”连接起来,如果是,然后连接没有“,”。

        List<String> e = new ArrayList<>(Arrays.asList("3333", "4444", "3333"));
        StringBuilder s = new StringBuilder();
    
        for (int i = 0; i < e.size(); i++) {
            s.append(e.get(i)).append((i == e.size() - 1) ? "" : ",");
        }
    

    【讨论】:

    • 或者更简洁:s.append(e.get(i) + ((i == e.size() - 1) ? "" : ",")); 里面可能有一些额外的 (),但这不是代码高尔夫...
    • 当你已经有一个StringBuilder 时,通过+ 使用字符串连接是很奇怪的。这是不一致的。如果你不关心性能(没关系,差别不大),那么在整个代码中使用+,否则,不要使用s.append(e.get(i)+",");,使用s.append(e.get(i)).append(",");
    【解决方案7】:

    如果我们谈论的是 Java 8,那么:

    Collection<String> e = asList("a", "b", "c");
    
    String result = e.stream().collect(Collectors.joining(",")); // a,b,c
    

    此方法可以应用于任何Collection 字符串,以及String.join()

    【讨论】:

      【解决方案8】:
      List<String> e = new ArrayList<String>();
          e.add("350000000000050287");
          e.add("392156486833253181");
          e.add("350000000000060764");
          Iterator iterator = e.iterator();
          String s="";
          int i = 1;
          while (iterator.hasNext()) {
              if (i == e.size()) {
                  s =s+iterator.next();
              } else {
                  s =s+iterator.next()+",";
              }
              i++;
          }
          System.out.println(s);
      

      350000000000050287,392156486833253181,350000000000060764

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-02-13
        • 2015-02-08
        • 2012-05-19
        • 2012-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-08
        相关资源
        最近更新 更多