使用 List<UnaryOperator<...>> 映射对象

Mapping an object using a List<UnaryOperator<...>>

我有一个 List<UnaryOperator<String>> 并且需要 map/transform 通过运算符列表传递一个字符串。我有以下功能 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.

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

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

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

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

reduce方法使用组合器实现并行。在并行流中,reduce 可以一次 运行 许多一元运算符,并使用提供的组合器组合结果。 combiner的存在是by-design,并没有过错

这段代码的问题不在于你在源代码中的两个地方表达了连接,而是你在 运行time 进行了很多次字符串连接,这造成了很多不必要的字符串。

您应该改用字符串生成器。由于 StringBuilder 是可变的,您不会创建很多 StringBuilder(除非您使用并行流,但那是另一回事),并且您将使用 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);

我可以建议另一种使用二元运算符连接字符串的方法,但该建议不太可能成为您代码片段的优化。

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

您可以利用 UnaryUperator<T> extends Function<T, T> 的优势并链接 Function::andThen 的多个调用以获得列表中所有内容的组合 UnaryOperator<String>

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);
  }
}

这个解决方案怎么样:

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