i am currently reading the O'reilly
Java 8 Lambdas is a really good book. i came across with a example like this.
i have a
private final BiFunction<StringBuilder,String,StringBuilder>accumulator=
(builder,name)->{if(builder.length()>0)builder.append(",");builder.append("Mister:").append(name);return builder;};
final Stream<String>stringStream = Stream.of("John Lennon","Paul Mccartney"
,"George Harrison","Ringo Starr");
final StringBuilder reduce = stringStream
.filter(a->a!=null)
.reduce(new StringBuilder(),accumulator,(left,right)->left.append(right));
System.out.println(reduce);
System.out.println(reduce.length());
this produce the right output.
Mister:John Lennon,Mister:Paul Mccartney,Mister:George Harrison,Mister:Ringo Starr
my question is regarded the reduce
method the last parameter which is a BinaryOperator
my question is which this parameter is used for? if i change by
.reduce(new StringBuilder(),accumulator,(left,right)->new StringBuilder());
the ouput is the same if i pass NULL
then N.P.E is returned.
what for this parameter is used for?
UPDATE
why if i run it on parallelStream
i am receiving differents results?
first run.
returned StringBuilder length = 420
second run
returned StringBuilder length = 546
third run
returned StringBuilder length = 348
and so on? why is this... should not return all the values at each iteration?
any help is hugely grateful.
thanks.
Reducing is the repeated process of combining all elements. reduce operation applies a binary operator to each element in the stream where the first argument to the operator is the return value of the previous application and second argument is the current stream element.
The BinaryOperator Interface<T> is a part of the java. util. function package which has been introduced since Java 8, to implement functional programming in Java. It represents a binary operator which takes two operands and operates on them to produce a result.
identity : Like the Stream. reduce operation, the identity element is both the initial value of the reduction and the default result if there are no elements in the stream. In this example, the identity element is 0 ; this is the initial value of the sum of ages and the default value if no members exist.
Identity is the default result of reduction if there are no elements in the stream. That's the reason, this version of reduce method doesn't return Optional because it would at least return the identity element. Ignoring this rule will result in unexpected outcomes.
The method reduce
in the interface Stream
is overloaded. The parameters for the method with three arguments are:
The combiner
supports parallel execution. Apparently, it is not used for sequential streams. However, there is no such guarantee. If you change your streams into parallel stream, I guess you will see a difference:
Stream<String>stringStream = Stream.of(
"John Lennon", "Paul Mccartney", "George Harrison", "Ringo Starr")
.parallel();
Here is an example of how the combiner
can be used to transform a sequential reduction into a reduction, that supports parallel execution. There is a stream with four String
s and acc
is used as an abbreviation for accumulator.apply
. Then the result of the reduction can be computed as follows:
acc(acc(acc(acc(identity, "one"), "two"), "three"), "four");
With a compatible combiner
, the above expression can be transformed into the following expression. Now it is possible to execute the two sub-expressions in different threads.
combiner.apply(
acc(acc(identity, "one"), "two"),
acc(acc(identity, "three"), "four"));
Regarding your second question, I use a simplified accumulator
to explain the problem:
BiFunction<StringBuilder,String,StringBuilder> accumulator =
(builder,name) -> builder.append(name);
According to the Javadoc for Stream::reduce, the accumulator
has to be associative. In this case, that would imply, that the following two expressions return the same result:
acc(acc(acc(identity, "one"), "two"), "three")
acc(acc(identity, "one"), acc(acc(identity, "two"), "three"))
That's not true for the above accumulator
. The problem is, that you are mutating the object referenced by identity
. That's a bad idea for the reduce
operation. Here are two alternative implementations which should work:
// identity = ""
BiFunction<String,String,String> accumulator = String::concat;
// identity = null
BiFunction<StringBuilder,String,StringBuilder> accumulator =
(builder,name) -> builder == null
? new StringBulder(name) : builder.append(name);
nosid's answer got it mostly right (+1) but I wanted to amplify a particular point.
The identity
parameter to reduce
must be an identity value. It's ok if it's an object, but if it is, it should immutable. If the "identity" object is mutated, it's no longer an identity! For more discussion of this point, see my answer to a related question.
It looks like this example originated from Example 5-19 of Richard Warburton, Java 8 Lambdas, O'Reilly 2014. If so, I shall have to have a word about this with the good Dr. Warburton.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With