英文:
Mapping an object using a List<UnaryOperator<...>>
问题
我有一个 List<UnaryOperator<String>>
,需要通过操作符列表映射/转换一个字符串。我有以下功能性的 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(
"", // 起始值
(value, op) -> op.apply(value), // 累加器
(value1, value2) -> value1.concat(value2) // 合并器
);
System.out.println(concatenatedString); // 预期输出 "abc"。
我关心的问题是字符串的连接在两个地方都有体现。第一处在每个 `UnaryOperator` 中,第二处在合并器参数中。我在想是否有更好的方法来实现这个?
英文:
I have a List<UnaryOperator<String>>
and need to map/transform a String by passing it through the list of operators. I have the following functional Java 11 code:
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.
The concern I have is the string concatenation is expressed in 2 places. First in each of the UnaryOperator
s and second in the combiner argument. Makes me wonder if there is a better way to do this?
答案1
得分: 3
你可以利用 UnaryOperator<T>
扩展了 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(""); // 结果为 "abc"
为了更清楚地了解其工作原理,以下是在 reduce
方法内部调用的部分:
new BinaryOperator<UnaryOperator<String>>() {
@Override
public UnaryOperator<String> apply(UnaryOperator<String> l, UnaryOperator<String> r) {
return string -> l.andThen(r).apply(string);
}
}
英文:
You can use the advantage that UnaryUperator<T> extends Function<T, T>
and chain multiple calls of Function::andThen
to get a composed UnaryOperator<String>
of all within the list:
UnaryOperator<String> mergedUnaryOperators = operators.stream()
.reduce((l, r) -> (string) -> l.andThen(r).apply(string))
.orElseGet(UnaryOperator::identity);
String output = mergedUnaryOperators.apply(""); // results in "abc"
To have a clearer picture how does it work, this is called inside the reduce
method:
new BinaryOperator<UnaryOperator<String>>() {
@Override
public UnaryOperator<String> apply(UnaryOperator<String> l, UnaryOperator<String> r) {
return string -> l.andThen(r).apply(string);
}
}
答案2
得分: 1
你可以使用andThen将这三个UnaryOperator
组合成一个单一的Function
,从而形成一个转换流水线。
Function<String, String> addABC = addA
.andThen(addB)
.andThen(addC);
System.out.println(addABC.apply("")); //输出 abc
如果你有许多要开始的UnaryOperator
,请参阅尼古拉斯的回答,了解如何将它们减少为一个单一的UnaryOperator
。
英文:
You can compose the three UnaryOperator
s into a single Function
using andThen to form a pipeline of transformation.
Function<String, String> addABC = addA
.andThen(addB)
.andThen(addc);
System.out.println(addABC.apply("")); //Prints abc
If you have many UnaryOperator
s to start with, see Nicholas' answer on how to reduce them to a single UnaryOperator
.
答案3
得分: 1
以下是翻译好的内容:
而这个解决方案呢:
Function<String, String> combineF = operators.stream()
.reduce(UnaryOperator.identity(), (l, r) -> (string) -> r.compose(l).apply(string));
System.out.println(combineF.apply(""));
英文:
And what about this solution:
Function<String, String> combineF = operators.stream()
.reduce(UnaryOperator.identity(), (l, r) -> (string) -> r.compose(l).apply(string));
System.out.println(combineF.apply(""));
答案4
得分: 0
reduce
方法使用合并器(combiner)来实现并行处理。在并行流中,reduce
可以同时运行许多一元操作符,并使用提供的合并器来合并结果。combiner
的存在是有意设计的,并没有问题。
这段代码的问题不在于你在源代码中两个地方都使用了连接操作,而是在运行时进行了大量的字符串连接操作,从而创建了许多不必要的字符串。
你应该改为使用字符串构建器(StringBuilder)。因为 StringBuilder
是可变的,所以你不会创建很多 StringBuilder
实例(除非使用并行流,但那是另外的故事),而且你将使用 collect
而不是 reduce
。还要注意从 UnaryOperator
改为了 Consumer
:
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, // 身份元素
(value, op) -> op.accept(value), // 累加器
StringBuilder::append // 合并器
);
System.out.println(concatenatedString);
英文:
The reduce
method uses the combiner to achieve parallelism. In a parallel stream, reduce
can run many of your unary operators at once, and use the provided combiner to combine the results. The existence of combiner
is by-design, and it is not at fault.
The problem with this code is not that you have expressed concatenation in two places in the source code, but rather that you are doing string concatenation a lot of times at runtime, which creates a lot of unnecessary strings.
You should use a string builder instead. Since StringBuilder
is mutable, you won't be creating a lot of StringBuilder
s (unless you use parallel streams, but that's another story), and you will be using collect
, rather than reduce
. Notice the other change from UnaryOperator
to Consumer
too:
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);
答案5
得分: 0
我可以提供另一种使用二进制运算符连接字符串的方法,但是在您的代码片段中,这个提案不太可能进行优化。
String concatenatedString = operators
.stream().reduce((op1, op2) -> (val) -> op1.apply(val) + op2.apply(val))
.get().apply("");
英文:
I can suggest another way to concatenate strings using the binary operator, but this proposal is unlikely to be an optimization in a snippet of your code.
String concatenatedString = operators
.stream().reduce((op1, op2) -> (val) -> op1.apply(val) + op2.apply(val))
.get().apply("");
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论