Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 reduce BinaryOperator what is used for?

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.

like image 849
chiperortiz Avatar asked Jun 01 '14 14:06

chiperortiz


People also ask

What is significance of reduce () in Java?

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.

What is BinaryOperator Java?

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.

What is identity in Reduce?

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.

What happens if a reduction operation has no identity element?

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.


2 Answers

The method reduce in the interface Stream is overloaded. The parameters for the method with three arguments are:

  • identity
  • accumulator
  • combiner

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 Strings 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);
like image 185
nosid Avatar answered Sep 28 '22 07:09

nosid


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.

like image 28
Stuart Marks Avatar answered Sep 28 '22 08:09

Stuart Marks