【问题标题】:Using streams to convert a list of objects into a string obtained from the toString method使用流将对象列表转换为从 toString 方法获得的字符串
【发布时间】:2014-09-13 00:15:36
【问题描述】:

Java 8 中有很多有用的新东西。例如,我可以用流遍历对象列表,然后对 Object 实例的特定字段中的值求和。例如

public class AClass {
  private int value;
  public int getValue() { return value; }
}

Integer sum = list.stream().mapToInt(AClass::getValue).sum();

因此,我想问是否有任何方法可以构建 String,将 toString() 方法的输出从实例中连接到一行中。

List<Integer> list = ...

String concatenated = list.stream().... //concatenate here with toString() method from java.lang.Integer class

假设list 包含整数123,我希望concatenated"123""1,2,3"

【问题讨论】:

标签: java java-8 java-stream


【解决方案1】:

一种简单的方法是将列表项附加到StringBuilder

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

StringBuilder b = new StringBuilder();
list.forEach(b::append);

System.out.println(b);

你也可以试试:

String s = list.stream().map(e -> e.toString()).reduce("", String::concat);

说明:map 将 Integer 流转换为 String 流,然后将其归约为所有元素的串联。

注意:这是normal reduction,执行时间为 O(n2)

为了获得更好的性能,请使用类似于 F. Böller 的答案的 StringBuildermutable reduction

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

参考:Stream Reduction

【讨论】:

  • 是的,不是内联解决方案,而是解决方案。
  • 不明白。你在期待什么?
  • 我不明白为什么“在 O(n2) 中执行的正常缩减”。对我来说,它“看起来”更像 O(n)...
  • @datahaki 我不是 Java 人,但我猜迭代字符串连接(减少)需要在每次迭代时进行数组(重新)分配,这使得它成为 O(n^2)。另一边的join 会在填充之前预先分配一个足够大的数组来存储最终字符串,使其成为 O(n)。
  • 非常好的提示。也许您可以使用“::”符号进行简化。所以,list.stream().map(Object::toString).reduce("", String::concat)。使用map e-&gt; e.toString() 有点多余。
【解决方案2】:

我将使用流 api 将整数流转换为单个字符串。一些提供的答案的问题是,由于字符串构建,它们产生了 O(n^2) 运行时。更好的解决方案是使用 StringBuilder,然后将字符串连接在一起作为最后一步。

//              Create a stream of integers 
    String result = Arrays.stream(new int[]{1,2,3,4,5,6 })                
            // collect into a single StringBuilder
            .collect(StringBuilder::new, // supplier function
                    // accumulator - converts cur integer into a string and appends it to the string builder
                    (builder, cur) -> builder.append(Integer.toString(cur)),
                    // combiner - combines two string builders if running in parallel
                    StringBuilder::append) 
            // convert StringBuilder into a single string
            .toString();

您可以通过将对象集合转换为单个字符串来进一步处理此过程。

// Start with a class definition
public static class AClass {
    private int value;
    public int getValue() { return value; }
    public AClass(int value) { this.value = value; }

    @Override
    public String toString() {
        return Integer.toString(value);
    }
}

// Create a stream of AClass objects
        String resultTwo = Arrays.stream(new AClass[]{
                new AClass(1),
                new AClass(2),
                new AClass(3),
                new AClass(4)
        })
                // transform stream of objects into a single string
                .collect(StringBuilder::new,
                        (builder, curObj) -> builder.append(curObj.toString()),
                        StringBuilder::append
                )
            // finally transform string builder into a single string
            .toString();

【讨论】:

    【解决方案3】:

    对于那些“加入字符串列表”的用例,String API 中有一个方法,你甚至不需要 Stream。

    List<String> myStringIterable = Arrays.asList("baguette", "bonjour");
    
    String myReducedString = String.join(",", myStringIterable);
    
    // And here you obtain "baguette,bonjour" in your myReducedString variable
    

    【讨论】:

      【解决方案4】:

      API 中有一个收集器joining。 这是Collectors中的静态方法。

      list.stream().map(Object::toString).collect(Collectors.joining(","))
      

      由于toString 的必要调用,并不完美,但可以。可以使用不同的分隔符。

      【讨论】:

      • 为了完整性:要让确切的代码正常工作,您需要import static java.util.stream.Collectors.joining;
      • 为什么我们需要映射和显式的`toString'?如果收集器期待 String 元素,则应隐式调用转换,不是吗?!
      【解决方案5】:

      StringListName = ObjectListName.stream().map( m -> m.toString() ).collect( Collectors.toList() );

      【讨论】:

        【解决方案6】:

        for 循环中测试Shail016bpedroso 答案(https://stackoverflow.com/a/24883180/2832140) 中建议的两种方法,简单的StringBuilder + append(String) 似乎比list.stream().map([...] 执行得快得多。

        示例:此代码遍历 Map&lt;Long, List&lt;Long&gt;&gt; 构建一个 json 字符串,使用 list.stream().map([...]

        if (mapSize > 0) {
            StringBuilder sb = new StringBuilder("[");
        
            for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {
        
                sb.append("{\"" + entry.getKey().toString() + "\":[");
                sb.append(entry.getValue().stream().map(Object::toString).collect(Collectors.joining(",")));
            }
            sb.delete(sb.length()-2, sb.length());
            sb.append("]");
            System.out.println(sb.toString());
        }
        

        在我的开发虚拟机上,junit 通常需要 0.35 到 1.2 秒来执行测试。而使用以下代码,需要 0.15 到 0.33 秒:

        if (mapSize > 0) {
            StringBuilder sb = new StringBuilder("[");
        
            for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {
        
                sb.append("{\"" + entry.getKey().toString() + "\":[");
        
                for (Long tid : entry.getValue()) {
                    sb.append(tid.toString() + ", ");
                }
                sb.delete(sb.length()-2, sb.length());
                sb.append("]}, ");
            }
            sb.delete(sb.length()-2, sb.length());
            sb.append("]");
            System.out.println(sb.toString());
        }
        

        【讨论】:

          【解决方案7】:
          List<String> list = Arrays.asList("One", "Two", "Three");
              list.stream()
                      .reduce("", org.apache.commons.lang3.StringUtils::join);
          

          或者

          List<String> list = Arrays.asList("One", "Two", "Three");
                  list.stream()
                          .reduce("", (s1,s2)->s1+s2);
          

          此方法还允许您从对象列表构建字符串结果 示例

          List<Wrapper> list = Arrays.asList(w1, w2, w2);
                  list.stream()
                          .map(w->w.getStringValue)
                          .reduce("", org.apache.commons.lang3.StringUtils::join);
          

          这里的reduce函数允许你有一些你想附加新字符串的初始值 示例:

           List<String> errors = Arrays.asList("er1", "er2", "er3");
                      list.stream()
                              .reduce("Found next errors:", (s1,s2)->s1+s2);
          

          【讨论】:

            【解决方案8】:

            以防万一有人在没有 java 8 的情况下尝试这样做,有一个非常好的技巧。 List.toString() 已经返回一个如下所示的集合:

            [1,2,3]

            根据您的具体要求,只要您的列表项不包含 [] 或 , ,就可以将其后处理为您想要的任何内容。

            例如:

            list.toString().replace("[","").replace("]","") 
            

            或者如果您的数据可能包含方括号:

            String s=list.toString();
            s = s.substring(1,s.length()-1) 
            

            会给你一个相当合理的输出。

            每行的一个数组项可以这样创建:

            list.toString().replace("[","").replace("]","").replaceAll(",","\r\n")
            

            我使用这种技术从一个小应用程序中的列表制作 html 工具提示,类似于:

            list.toString().replace("[","<html>").replace("]","</html>").replaceAll(",","<br>")
            

            如果你有一个数组,那么从 Arrays.asList(list).toString() 开始

            我完全承认这不是最优的,但它并没有你想象的那么低效,而且阅读和理解起来非常简单。但是,它非常不灵活 - 特别是如果您的数据可能包含逗号,请不要尝试使用 replaceAll 分隔元素,如果您的数据中有方括号,请使用子字符串版本,但对于数字数组,它几乎完美。

            【讨论】:

            • 虽然这通常会起作用,但不能保证 — List 不强制执行 toString() 的结构。 AbstractCollection 但是,默认情况下确实使用这种结构,我认为 Java SE 中所有通用的 List 实现也是如此。例如,Spring 中的 org.springframework.util.AutoPopulatingList 没有实现 toString() ,因此会返回例如“org.springframework.util.AutoPopulatingList@170a1”。
            • Arrays.toString() 会是比Arrays.asList(list).toString() 更好的选择,因为它被定义为返回一个等效的字符串,它更简洁,并且不需要额外创建对象。
            • @M.Justin 对于这个答案的数组部分来说这不是一个坏点,但是一个简单的“列表”呢?你不能真正使用 Arrays.toString() 。您如何建议在您提到的 AutoPopulatingList 之类的东西上使用这种技术?也许:新的 ArrayList(autoPopulatingList).toString()?或者我猜你可以把它转换成一个数组。也许如果您遇到这样的问题,您应该希望您使用的是 1.8 或更高版本,并且可以使用此线程中的其他答案之一...
            【解决方案9】:

            使用 Java 8+

            String s = Arrays.toString(list.stream().toArray(AClass[]::new));
            

            不是最高效的,但它是一个用少量代码的解决方案。

            【讨论】:

              【解决方案10】:

              另外,你也可以这样做。

                  List<String> list = Arrays.asList("One", "Two", "Three");
                  String result = String.join(", ", list);
                  System.out.println(result);
              

              【讨论】:

              • 您的解决方案仅在列表是 List 时才有效。 OP没有指定这样的类。在他的问题中,甚至指向 List
              【解决方案11】:
              String actual = list.stream().reduce((t, u) -> t + "," + u).get();
              

              【讨论】:

              • 通常最好在您的帖子中添加解释代码如何工作的说明。这使新开发人员能够了解代码的作用。
              • @CalebKleveter 它将 List 简化为单个 String 并用逗号 (,) 分隔每个元素。
              • 您的解决方案仅在列表是 List 时才有效。 OP没有指定这样的类。在他的问题中,甚至指向 List 。在这种情况下,您的代码无法编译。
              【解决方案12】:

              我们可以试试这个吗?

              public static void main(String []args){
                      List<String> stringList = new ArrayList<>();
                      for(int i=0;i< 10;i++){
                          stringList.add(""+i);
                      }
                      String stringConcated = String.join(",", stringList);
                      System.out.println(stringConcated);
              
                  }
              

              【讨论】:

              • 您的解决方案仅在列表是 List 时才有效。 OP没有指定这样的类。在他的问题中,甚至指向 List
              【解决方案13】:

              其他答案都很好。但是,您也可以将Collectors.toList() 作为参数传递给Stream.collect(),以将元素作为ArrayList 返回。

              System.out.println( list.stream().map( e -> e.toString() ).collect( toList() ) );
              

              【讨论】:

              • 如果你使用System.out.print(ln)(list),它将使用元素的toString()方法来打印列表。所以,这段代码只是在重复 List 的toString 内部发生的事情。
              • 应该是 Collectors.toList() 除非你正在导入静态。
              • 是的...我为示例导入了静态。我在答案正文中声明了“Collectors.toList()”... ;)
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-12-28
              • 1970-01-01
              • 2015-02-25
              • 2017-10-07
              相关资源
              最近更新 更多