如何在 Java 8 中引用 reduce() 操作的结果?

Posted

技术标签:

【中文标题】如何在 Java 8 中引用 reduce() 操作的结果?【英文标题】:How to reference the result of reduce() operation in Java 8? 【发布时间】:2019-06-27 01:20:14 【问题描述】:

我试图在 Java8 中编写一个 mkString 函数,这是一个 Scala 的有用 mkString 并遇到了 2 个我可以使用一些帮助的问题:

    我无法将mkString 的第一个参数设置为像Collection<Object> c 这样的通用集合引用,并让调用者调用任何类型的集合。

    无法通过内联引用reduce() 返回的结果来访问结果的长度以删除多余的前导分隔符。

代码如下:

public static void main(String[] args) 
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    System.out.println(mkString(numbers, ","));



public static String mkString(Collection<Integer> c, String sep) 
    return c.stream()
            .map(e -> String.valueOf(e))
            .reduce("", (a, b) -> a + sep + b)
            .substring(1, <<>>.length);

【问题讨论】:

使用 reduce 来构建字符串的效率非常低。每个元素都需要累加器字符串的完整副本。最好使用专用的连接函数,它可以使用字符串生成器之类的东西。 【参考方案1】:

请注意,如果您这样做不是为了自学,而是为了在一些生产代码中实际使用它,您可能需要考虑内置的 Collectors.joining 收集器:

String result = numbers.stream()
    .map(Object::toString)
    // or
    //   .map(x -> x.toString())  // exactly the same
    // or
    //   .map(String::valueOf)    // handles nulls by turning them to the string "null"
    .collect(Collectors.joining(","));

它有几个重载,类似于 Scala 的 mkString。尽管如此,这个收集器只接受CharSequences,因此您需要将值显式转换为字符串作为单独的map 步骤。

此外,还有String.join 方法,它也适用于CharSequences 的集合。如果您特别有其中一个(例如List&lt;String&gt;),使用此方法可能更方便,而不是先将集合转换为流:

List<String> strings = ...;

String result = String.join(",", strings);

// vs

String result = strings.stream().collect(Collectors.joining(","))

【讨论】:

这不会编译。 Collectors::joining 期待 CharSequence,您还需要提供 mapper 如果您正在这样做是为了自学,请考虑自己编写等效的收集器(或者不完全相同来解决尤金提到的问题)。 尽管这个解决方案需要.map,但我仍然认为这是最好的解决方案,因为它是性能最高的,因为它在内部使用字符串生成器,而不是连接字符串 确实,我错过了它想要CharSequences,所以它实际上与String.join 非常相似。我已经修复了我的答案,包括map 电话。 即使是出于教育目的,也值得指出collect(Collectors.joining(","))正确 解决方案,而reduce("", (a, b) -&gt; a + "," + b) 的功能违反了关联性约束。【参考方案2】:

如果我没记错我的 java,您可以将参数类型声明为 Collection&lt;?&gt; 以便能够传递任何对象的集合。

至于咬掉分隔符,我想,.substring(1) 会做你想做的事。

【讨论】:

与 Collection> 方法相比,docs.oracle.com/javase/tutorial/extra/generics/methods.html 似乎更倾向于泛型方法。 T? 在这种情况下是完全一样的。所以,这是一个品味问题。我个人更喜欢?,因为它清楚地表明实现不使用或不需要类型信息,并且集合旨在进行变异。它也是呼叫站点差异的常见指标。 @212:我认为您一定是误读了您链接到的页面。在“[t]返回类型不依赖于类型参数,也不依赖于方法的任何其他参数”的情况下,“应该使用通配符”非常明确,并且将多个段落专门用于该指导。所以迪玛说的很对。 @Dima 用更简单的话来说(IIRC 也存在于有效的 java 中):如果类型参数只使用一次,则可以用通配符替换。 如果分隔符由多个字符组成,.substring(1) 将不起作用【参考方案3】:

你可以这样做:

public static <T>  String mkString(Collection<T> c, String sep)  // generic impl
    return c.stream()
            .map(String::valueOf)
            .reduce("", (a, b) -> a + sep + b)
            .substring(1); // substring implementation to strip leading character

【讨论】:

感谢您解决这两个问题。但是,这是否意味着没有语法可以访问reduce()的结果而不进行赋值呢? T 不是必需的。 Collection&lt;?&gt; 也可以。 @nullpointer 这不是关联的,因此违反了规范...例如,您希望从String s = Stream.of("a", "b", "c", "d") .parallel() .reduce("", (a, b) -&gt; a + "-" + b); System.out.println(s); 打印什么? @212 好吧,这取决于您主要要执行的操作。 @nullpointer 即使您不确定是否需要:1)文档要求它 2)尝试我上面给出的代码并查看结果 - 但首先尝试看看您是否可以预测它【参考方案4】:

任何类型 java中的collection表示Collection&lt;?&gt;,语义上与Collection&lt;T&gt;相同(在你的情况下),据说如果类型参数只使用一次)它可以安全地用通配符替换。但是,由于您希望能够连接任何集合,您还应该要求调用者提供一个Function,它将从该类型转换为字符串表示,因此您的方法将变为:

public static <T> String mkString(Collection<T> c,
                                  Function<T, ? extends CharSequence> mapper,
                                  String sep) 
    return c.stream()
            .map(mapper)
            .collect(Collectors.joining(sep));


【讨论】:

【参考方案5】:

您可以将String.join 与泛型类型一起使用:

public static <T> String mkString(Collection<T> c, String sep) 
    return String.join(sep, c.stream()
                             .map(e -> String.valueOf(e))
                             .collect(Collectors.toList()));

Here it is 作用于字符串和其他对象。

【讨论】:

您正在收集到List,只是为了能够致电String::join?为此目的有Collectors.joining。并且您假设String::valueOf 将提供对象的任何有意义的字符串表示,这可能不成立。更好的方法是也传递一个映射器。 @Eugene 忘记了collectors.joining,但现在它已经是一个答案,所以我会保持原样。传递映射器虽然有用,但超出了 imo 的范围。感谢您的反馈。

以上是关于如何在 Java 8 中引用 reduce() 操作的结果?的主要内容,如果未能解决你的问题,请参考以下文章

执行 map-reduce 操作的通用方法。 (Java-8)

为啥在java 8中转换类型的reduce方法需要一个组合器

java 使用reduce和Integer.max查找最大值java 8

廖雪峰老师python教程——map/reduce

流上的 reduce() 操作似乎正在修改数据源(列表)Stream API Java 8

HashMap 上的 Java 8 Map Reduce 作为 lambda