使用streams().reduce 从ArrayList<Integer> 构建一个字符串?

Posted

技术标签:

【中文标题】使用streams().reduce 从ArrayList<Integer> 构建一个字符串?【英文标题】:Using streams().reduce to build a string from ArrayList<Integer>? 【发布时间】:2021-06-26 03:16:01 【问题描述】:

JavaScript 中,我们可以使用 reducer 构建具有其他类型的字符串(例如 num 到字符串):

const string = [1,2,3,4,5].reduce((acc,e) => acc += e, "") //"12345"

Java 中,从其他类型构建字符串时,这种模式并不容易:

ArrayList<Integer> arrayListOfIntegers = (ArrayList<Integer>) Arrays.asList(1,2,3,4);
String string = arrayListOfIntegers.stream().reduce("", (String acc, Integer e) -> acc += e); // acc += e throws error

错误是:

“错误的返回类型:字符串无法转换为整数”

这种模式在 Java 中是不可能的吗?

【问题讨论】:

Java 是一种强类型语言,您不能只是将整数混入字符串并期望编译器知道您的意思是连接还是加法。此外,如果您阅读Stream.reduce 的JavaDoc,您会发现所有涉及的参数都必须属于同一类型。所以你需要先将 Integer 映射到 String 才能将其缩减为字符串。 使用+= 是一种混淆。您的实际意图是(acc, e) -&gt; acc + e,但您选择使用+= 将导致对acc 参数变量的无意义修改,该变量将永远不会被再次读取。除此之外,在 Java 中你需要reduce("", (acc,e) -&gt; acc + e, (s1,s2) -&gt; s1 + s2)。这可行,但对于具有大量元素的流来说效率非常低。如果您喜欢简单,请使用 map(String::valueOf).collect(Collectors.joining()),或使用 collect(StringBuilder::new, StringBuilder::append, StringBuilder::append) .toString() 以获得最高效率。 【参考方案1】:

在 Java 中,您可以使用 Collectors.joining 简单地收集 Stream

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

public class Main 
    public static void main(String[] args) 
        List<Integer> arrayListOfIntegers = Arrays.asList(1, 2, 3, 4);
        String str = arrayListOfIntegers.stream().map(String::valueOf).collect(Collectors.joining());
        System.out.println(str);
    

输出:

1234

【讨论】:

【参考方案2】:

您正在尝试将整数列表减少到String。首先将这些整数映射到字符串,然后 reduce:

List<Integer> list = Arrays.asList(1,2,3,4);
String value = list.stream()
                   .map(String::valueOf)
                   .reduce("", String::concat);

您可以使用String::concat 代替(acc,e) -&gt; acc+e。此外,lambda 表达式中的类型不是强制性的。

【讨论】:

.collect(Collectors.joining()); 而不是reduce。我认为 concat 版本将具有 O(n^2) 复杂性,因为它会为每个元素创建一个新字符串(本身就是 O(n))。【参考方案3】:

我想最好的方法是使用 @Thomas 和 @Arvind Kumar Avinash 提供的 Collectors.joining。但作为替代解决方案,我可以建议使用 collect 方法和 StringBuilder 类,它不会像 reduce case 那样每次都生成新的 String 对象:

List<Integer> list = Arrays.asList(1,2,3,4);
String value = 
       list.stream()
           .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
           .toString();

【讨论】:

【参考方案4】:

我建议了这个代码:

public static void main(String[] args) 
    List<Integer> arrayListOfIntegers = Arrays.asList(1,2,3,4);
    String string = arrayListOfIntegers.stream().reduce("",(sum,elm)-> sum += elm.toString(),(sum1,sum2) -> sum1+sum2);
    System.out.println(string);

【讨论】:

【参考方案5】:

在其他几个选项中,这是我的贡献,当您有 int 列表时,即原始值,请使用[IntStream][1] 这是我尝试过的方法。 rangeClosed 包括最后一个数字。 mapToObj 是中间操作,collect 是终端操作。

import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class SampleIntStream 
    public static void main(String[] args) 
        System.out.println(getComposedString(1, 5));
    

    public static String getComposedString(int from, int to) 
        return IntStream
                .rangeClosed(from, to)
                .mapToObj(String::valueOf)
                .collect(Collectors.joining());
    

【讨论】:

以上是关于使用streams().reduce 从ArrayList<Integer> 构建一个字符串?的主要内容,如果未能解决你的问题,请参考以下文章

将 Streams 与原始数据类型和相应的包装器一起使用

Array.reduce()方法的使用

前端开发Array.prototype.reduce()的详细解析&使用

Array.prototype.reduce 的理解与实现

使用 Array.reduce() 通过多个键对数组进行分组

如何正确使用 TypeScript 和 Array.prototype.reduce?