【问题标题】:Mapping an object using a List<UnaryOperator<...>>使用 List<UnaryOperator<...>> 映射对象
【发布时间】:2020-09-26 13:46:59
【问题描述】:

我有一个List&lt;UnaryOperator&lt;String&gt;&gt;,需要通过运算符列表来映射/转换一个字符串。我有以下功能性 Java 11 代码:

UnaryOperator<String> addA = (startString) -> startString + "a";
UnaryOperator<String> addB = (startString) -> startString + "b";
UnaryOperator<String> addc = (startString) -> startString + "c";
List<UnaryOperator<String>> operators = List.of(addA, addB, addc);
String concatenatedString =
    operators
        .stream()
        .reduce(
            "", // identity
            (value, op) -> op.apply(value), // accumulator
            (value1, value2) -> value1.concat(value2) // combiner
        );
System.out.println(concatenatedString); // prints "abc" as expected.

我担心的是字符串连接在两个地方表示。第一个在每个 UnaryOperators 中,第二个在 combiner 参数中。让我想知道是否有更好的方法来做到这一点?

【问题讨论】:

    标签: java java-11 functional-interface unary-operator


    【解决方案1】:

    您可以利用UnaryUperator&lt;T&gt; extends Function&lt;T, T&gt; 的优势并链接Function::andThen 的多个调用来获得列表中所有UnaryOperator&lt;String&gt; 的组合:

    UnaryOperator<String> mergedUnaryOperators = operators.stream()
                .reduce((l, r) -> (string) -> l.andThen(r).apply(string))
                .orElseGet(UnaryOperator::identity);
    
    String output = mergedUnaryOperators.apply("");      // results in "abc"
    

    为了更清楚地了解它是如何工作的,这在reduce 方法中被调用:

    new BinaryOperator<UnaryOperator<String>>() {
      @Override
      public UnaryOperator<String> apply(UnaryOperator<String> l, UnaryOperator<String> r) {
         return string -> l.andThen(r).apply(string);
      }
    }
    

    【讨论】:

      【解决方案2】:

      您可以使用andThen 将三个UnaryOperators 组合成一个Function 以形成转换管道。

      Function<String, String> addABC = addA
                  .andThen(addB)
                  .andThen(addc);
      System.out.println(addABC.apply("")); //Prints abc
      

      如果您有很多 UnaryOperators 开头,请参阅 Nicholas 的回答,了解如何将它们减少为单个 UnaryOperator

      【讨论】:

        【解决方案3】:

        reduce 方法使用组合器来实现并行性。在并行流中,reduce 可以同时运行许多一元运算符,并使用提供的组合器来组合结果。 combiner 的存在是设计使然,没有错。

        这段代码的问题不是你在源代码中的两个地方都表达了连接,而是你在运行时做了很多次字符串连接,这会产生很多不必要的字符串。

        您应该改用字符串生成器。由于StringBuilder 是可变的,因此您不会创建很多StringBuilders(除非您使用并行流,但这是另一回事),您将使用collect,而不是reduce。注意从UnaryOperatorConsumer 的另一个变化:

        Consumer<StringBuilder> addA = (startString) -> startString.append("a");
        Consumer<StringBuilder> addB = (startString) -> startString.append("b");
        Consumer<StringBuilder> addc = (startString) -> startString.append("c");
        List<Consumer<StringBuilder>> operators = List.of(addA, addB, addc);
        StringBuilder concatenatedString =
            operators
                .stream()
                .collect(
                    StringBuilder::new, // identity
                    (value, op) -> op.accept(value), // accumulator
                    StringBuilder::append // combiner
                );
        System.out.println(concatenatedString);
        

        【讨论】:

          【解决方案4】:

          我可以建议另一种使用二元运算符连接字符串的方法,但该建议不太可能是对代码的 sn-p 的优化。

          String concatenatedString = operators
                          .stream().reduce((op1, op2) -> (val) -> op1.apply(val) + op2.apply(val))
                          .get().apply("");
          

          【讨论】:

            【解决方案5】:

            那么这个解决方案呢:

            Function<String, String> combineF = operators.stream()
                .reduce(UnaryOperator.identity(), (l, r) -> (string) ->  r.compose(l).apply(string));
            System.out.println(combineF.apply(""));
            

            【讨论】:

              猜你喜欢
              • 2020-04-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-11-03
              • 1970-01-01
              • 2017-03-03
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多