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

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

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

  2. 无法通过内联引用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 来构建字符串的效率非常低。每个元素都需要累加器字符串的完整副本。最好使用专用的连接函数,它可以使用字符串生成器之类的东西。

标签: java scala lambda java-stream 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 的范围。感谢您的反馈。
    猜你喜欢
    • 1970-01-01
    • 2017-11-06
    • 2018-12-13
    • 2015-08-29
    • 2019-09-22
    • 2016-03-15
    • 1970-01-01
    • 2021-10-14
    • 2018-11-16
    相关资源
    最近更新 更多