【问题标题】:Converting List<Integer> to List<String>将 List<Integer> 转换为 List<String>
【发布时间】:2010-09-06 07:47:22
【问题描述】:

我有一个整数列表List&lt;Integer&gt;,我想将所有整数对象转换为字符串,从而完成一个新的List&lt;String&gt;

当然,我可以创建一个新的 List&lt;String&gt; 并循环遍历每个整数调用 String.valueOf() 的列表,但我想知道是否有更好(阅读:更自动)的方法干吗?

【问题讨论】:

    标签: java string collections integer


    【解决方案1】:

    使用Google Collections from Guava-Project,您可以在Lists 类中使用transform 方法

    import com.google.common.collect.Lists;
    import com.google.common.base.Functions
    
    List<Integer> integers = Arrays.asList(1, 2, 3, 4);
    
    List<String> strings = Lists.transform(integers, Functions.toStringFunction());
    

    transform 返回的List 是支持列表上的一个视图 - 转换将应用于对转换列表的每次访问。

    请注意,Functions.toStringFunction() 在应用于 null 时会抛出 NullPointerException,因此只有在您确定列表不包含 null 时才使用它。

    【讨论】:

    • 如果Functions.toStringFunction()旁边有更多现成的函数就好了
    • 干净但可能没有那么快..每个值额外调用 1 个函数?
    • HotSpot 可以内联函数调用 - 所以如果调用足够多,应该不会有什么不同。
    • 我不会对此投反对票,因为它确实是一个解决方案。但鼓励人们添加库依赖项来解决如此简单的任务对我来说是不行的。
    • 如果您已经在我们的解决方案中使用 Guava,那就太好了。
    【解决方案2】:

    Java 8 的解决方案。比 Guava 长一点,但至少您不必安装库。

    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    //...
    
    List<Integer> integers = Arrays.asList(1, 2, 3, 4);
    List<String> strings = integers.stream().map(Object::toString)
                                            .collect(Collectors.toList());
    

    对于 Java 11,

    List<String> strings = integers.stream().map(Object::toString)
                                            .collect(Collectors.toUnmodifiableList());
    

    还是没有map方便的方法,真的吗?

    【讨论】:

    • 虽然对于 toString 示例来说这有点长,但对于 Guava 的函数库不支持的转换,它最终会更短。自定义函数仍然很简单,但它的代码比这个 Java 8 流要多得多
    • 对于未来的读者,这也是“反向”的方式。谢谢特雷卡兹!集合 键 = 等等等等等等; Collection keyInts = keys.stream().map(Integer::parseInt).collect(Collectors.toList());
    【解决方案3】:

    据我所知,迭代和实例化是唯一的方法。类似的东西(对于其他人可能的帮助,因为我相信你知道如何做到这一点):

    List<Integer> oldList = ...
    /* Specify the size of the list up front to prevent resizing. */
    List<String> newList = new ArrayList<>(oldList.size());
    for (Integer myInt : oldList) { 
      newList.add(String.valueOf(myInt)); 
    }
    

    【讨论】:

    • 简单的时候才叫美。
    • 原发帖人似乎表明他已经想到了这个,但认为这个解决方案过于复杂或乏味。但我很难想象还有什么比这更容易的。是的,有时您必须编写 3 或 4 行代码才能完成工作。
    • 但这会将您绑定到 ArrayList。这可以使用与原始列表相同的实现来完成吗?
    • @Andreas oldList.getClass().newInstance() 会做
    • 干净整洁。
    【解决方案4】:

    您所做的很好,但如果您觉得需要“Java-it-up”,您可以使用 Transformer 和来自 Apache Commonscollect method,例如:

    public class IntegerToStringTransformer implements Transformer<Integer, String> {
       public String transform(final Integer i) {
          return (i == null ? null : i.toString());
       }
    }
    

    ..然后..

    CollectionUtils.collect(
       collectionOfIntegers, 
       new IntegerToStringTransformer(), 
       newCollectionOfStrings);
    

    【讨论】:

    • CollectionUtils.collect(collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer());但是,StringValueTransformer 使用 String.valueOf ...
    • 除非在 apache 集合上完成了新的工作,否则他们不做泛型。
    • 这真的是Java-ing-it down。这不是惯用的 Java,更像是函数式编程。也许当我们在 Java 8 中获得闭包时,您可以称其为惯用的 Java。
    • 您肯定希望使用 Collections4(而不是旧的 3.x 集合)来支持泛型:commons.apache.org/proper/commons-collections/apidocs/org/…
    • 定义一个新类只是为了“更 OOP 或惯用”......我看不出这比简单的 for-each 循环更好。它需要更多代码并将功能移开(可以通过匿名类来缓解,但仍然如此)。这种函数式风格只有在有合适的语法(即自 Java 8 以来的 lambda 表达式)时才开始变得有用,就像函数式语言已经提供了几十年一样。
    【解决方案5】:

    String.valueOf 的来源表明:

    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }
    

    这并不重要,但我会使用 toString。

    【讨论】:

      【解决方案6】:

      我会使用 .toString(); 而不是使用 String.valueOf它避免了@johnathan.holland 描述的一些自动装箱

      javadoc 说 valueOf 返回与 Integer.toString() 相同的东西。

      List<Integer> oldList = ...
      List<String> newList = new ArrayList<String>(oldList.size());
      
      for (Integer myInt : oldList) { 
        newList.add(myInt.toString()); 
      }
      

      【讨论】:

      • 正如 Tom Hawtin 在“获胜”答案中指出的那样,不能实例化 List 因为它只是一个接口。
      • 嘿,我知道。只是我没有尝试就编写了代码。我会在我的回答中解决它。
      【解决方案7】:

      这是一个单线解决方案,无需使用非 JDK 库作弊。

      List<String> strings = Arrays.asList(list.toString().replaceAll("\\[(.*)\\]", "$1").split(", "));
      

      【讨论】:

        【解决方案8】:

        另一个使用 Guava 和 Java 8 的解决方案

        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<String> strings = Lists.transform(numbers, number -> String.valueOf(number));
        

        【讨论】:

          【解决方案9】:

          不是核心 Java,也不是泛型化的,但流行的 Jakarta commons 集合库为此类任务提供了一些有用的抽象。具体来说,看看收集方法

          CollectionUtils

          如果您已经在项目中使用公共集合,则需要考虑一些事项。

          【讨论】:

          • 永远不要使用 Apache 集合。它们是旧的、过时的、不是类型安全的,而且写得不好。
          【解决方案10】:

          对于在jsight的回答中关注“拳击”的人:没有。此处使用String.valueOf(Object),并且从未执行过对int 的拆箱操作。

          您使用Integer.toString() 还是String.valueOf(Object) 取决于您希望如何处理可能的空值。您想抛出异常(可能),还是列表中有“空”字符串(可能)。如果是前者,你想抛出NullPointerException 还是其他类型?

          另外,jsight 回复中的一个小缺陷:List 是一个接口,你不能在上面使用 new 运算符。在这种情况下,我可能会使用java.util.ArrayList,尤其是因为我们预先知道列表可能有多长。

          【讨论】:

            【解决方案11】:
            List<String> stringList = integerList.stream().map((Object s)->String.valueOf(s)).collect(Collectors.toList())
            

            【讨论】:

            • @RequiresApi(api = Build.VERSION_CODES.N)
            • @Adnan 您可能指的是 Android 使用情况。 OP一般都在问。所以,Java 8+ 应该足够了
            【解决方案12】:

            使用原始列表中的forEach方法稍微简洁的解决方案:

                List<Integer> oldList = Arrays.asList(1, 2, 3, 4, 5);
                List<String> newList = new ArrayList<>(oldList.size());
                oldList.forEach(e -> newList.add(String.valueOf(e)));
            

            【讨论】:

              【解决方案13】:

              @Jonathan:我可能弄错了,但我相信 String.valueOf() 在这种情况下会调用 String.valueOf(Object) 函数,而不是被装箱到 String.valueOf(int)。 String.valueOf(Object) 如果为 null,则返回“null”,如果为非 null,则调用 Object.toString(),这不应该涉及装箱(尽管显然涉及到实例化新的字符串对象)。

              【讨论】:

                【解决方案14】:

                我认为将 Object.toString() 用于除调试以外的任何目的可能是一个非常糟糕的主意,即使在这种情况下两者在功能上是等效的(假设列表没有空值)。开发人员可以在没有任何警告的情况下随意更改任何 toString() 方法的行为,包括标准库中任何类的 toString() 方法。

                甚至不用担心装箱/拆箱过程带来的性能问题。如果性能很关键,只需使用数组。如果它真的很重要,请不要使用 Java。试图智取 JVM 只会让人心痛。

                【讨论】:

                  【解决方案15】:

                  仅供专家解答:

                      List<Integer> ints = ...;
                      String all = new ArrayList<Integer>(ints).toString();
                      String[] split = all.substring(1, all.length()-1).split(", ");
                      List<String> strs = Arrays.asList(split);
                  

                  【讨论】:

                  • 这行得通,但代价是效率低下。 Java字符串是每个字符两个字节,所以“,”在计算整数本身之前为每个整数添加了一个四字节的固定成本......除此之外。
                  • 我认为正则表达式在原始 CPU 周期效率方面可能是一个更大的问题。在内存方面,我猜一个合理的实现(假设String 的“Sun”不合理实现)将共享相同的后备数组(来自all),所以实际上内存效率非常高,这对于长期表现。除非您当然只想保留其中一个元素...
                  【解决方案16】:

                  Lambdaj 允许以非常简单易读的方式做到这一点。例如,假设您有一个 Integer 列表,并且您想将它们转换为相应的 String 表示形式,您可以编写类似的内容;

                  List<Integer> ints = asList(1, 2, 3, 4);
                  Iterator<String> stringIterator = convertIterator(ints, new Converter<Integer, String> {
                      public String convert(Integer i) { return Integer.toString(i); }
                  }
                  

                  Lambdaj 仅在您对结果进行迭代时应用转换函数。

                  【讨论】:

                    【解决方案17】:

                    你无法避免“拳击开销”; Java 的仿通用容器只能存储对象,因此您的 int 必须装箱成整数。原则上它可以避免从 Object 到 Integer 的向下转换(因为它没有意义,因为 Object 对于 String.valueOf 和 Object.toString 都足够好),但我不知道编译器是否足够聪明来做到这一点。从 String 到 Object 的转换应该或多或少是无操作的,所以我不愿意担心那个。

                    【讨论】:

                    • 编译器不够聪明,无法做到这一点。当 javac 运行时,它实际上剥离了所有泛型类型信息。泛型集合的底层实现总是存储对象引用。您实际上可以省略 参数化并获得“原始”类型。 “List l = new List()”与“List l = new List()”。当然,这意味着“List l = (List)new List()”实际上会编译并运行,但显然非常危险。
                    【解决方案18】:

                    只是为了好玩,一个使用 jsr166y fork-join 框架的解决方案,应该在 JDK7 中。

                    import java.util.concurrent.forkjoin.*;
                    
                    private final ForkJoinExecutor executor = new ForkJoinPool();
                    ...
                    List<Integer> ints = ...;
                    List<String> strs =
                        ParallelArray.create(ints.size(), Integer.class, executor)
                        .withMapping(new Ops.Op<Integer,String>() { public String op(Integer i) {
                            return String.valueOf(i);
                        }})
                        .all()
                        .asList();
                    

                    (免责声明:未编译。规范未最终确定。等等)

                    不太可能在 JDK7 中是一些类型推断和语法糖,以使 withMapping 调用不那么冗长:

                        .withMapping(#(Integer i) String.valueOf(i))
                    

                    【讨论】:

                      【解决方案19】:

                      这是一个非常基本的事情,我不会使用外部库(它会导致您的项目中出现您可能不需要的依赖项)。

                      我们有一类专门用于完成此类工作的静态方法。因为这方面的代码非常简单,所以我们让 Hotspot 为我们做优化。这似乎是我最近代码中的一个主题:编写非常简单(直接)的代码,让 Hotspot 发挥它的魔力。我们很少会遇到此类代码的性能问题 - 当新的 VM 版本出现时,您会获得所有额外的速度优势等。

                      尽管我很喜欢 Jakarta 系列,但它们不支持泛型并且使用 1.4 作为 LCD。我对 Google Collections 持谨慎态度,因为它们被列为 Alpha 支持级别!

                      【讨论】:

                        【解决方案20】:

                        我没有看到任何遵循空间原则的解决方案 复杂。如果整数列表有大量元素,那么它是 大问题。

                        It will be really good to remove the integer from the List<Integer> and free
                        the space, once it's added to List<String>.
                        

                        我们可以使用迭代器来实现相同的目的。

                            List<Integer> oldList = new ArrayList<>();
                            oldList.add(12);
                            oldList.add(14);
                            .......
                            .......
                        
                            List<String> newList = new ArrayList<String>(oldList.size());
                            Iterator<Integer> itr = oldList.iterator();
                            while(itr.hasNext()){
                                newList.add(itr.next().toString());
                                itr.remove();
                            }
                        

                        【讨论】:

                          【解决方案21】:

                          使用流: 如果说结果是整数列表(List&lt;Integer&gt; result),那么:

                          List<String> ids = (List<String>) result.stream().map(intNumber -> Integer.toString(intNumber)).collect(Collectors.toList());
                          

                          解决方法之一。希望这会有所帮助。

                          【讨论】:

                            【解决方案22】:

                            我只是想提出一个面向对象的解决方案来解决这个问题。

                            如果你为领域对象建模,那么解决方案就在领域对象中。这里的域是我们想要字符串值的整数列表。

                            最简单的方法是根本不转换列表。

                            话虽如此,为了在不转换的情况下进行转换,请将原始 Integer 列表更改为 List of Value,其中 Value 看起来像这样......

                            class Value {
                                Integer value;
                                public Integer getInt()
                                {
                                   return value;
                                }
                                public String getString()
                                {
                                   return String.valueOf(value);
                                }
                            }
                            

                            这将比复制列表更快,占用更少的内存。

                            祝你好运!

                            【讨论】:

                              猜你喜欢
                              • 2022-09-30
                              • 2012-05-29
                              • 2013-08-03
                              • 2023-03-26
                              • 1970-01-01
                              • 2020-02-17
                              • 2015-11-03
                              相关资源
                              最近更新 更多