Win a copy of Terraform in Action this week in the Cloud forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Tim Cooke
  • Campbell Ritchie
  • Paul Clapham
  • Ron McLeod
  • Liutauras Vilda
Sheriffs:
  • Jeanne Boyarsky
  • Rob Spoor
  • Bear Bibeault
Saloon Keepers:
  • Jesse Silverman
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
Bartenders:
  • Piet Souris
  • Al Hobbs
  • salvin francis

Clarification Needed for Behavior of the reduce() Stream Operation/Method

 
Ranch Hand
Posts: 113
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The code below contains the two- and three-parameter reduce() stream operation/method, on lines 1 through 3 and lines 5 through 7, respectively. While the three-parameter method successfully reduces the stream items to the stringwolf,” the compiler reports an error when an attempt is made to use the two-parameter method to reduce the stream items shown in the code. The error message reported by the compiler is also provided below.
The two-parameter reduce() stream operation/method requires the following, when the method is invoked: The type of the first, identity argument passed to the method must be the same as the (single) type of the BinaryOperator lambda passed as the second, accumulator argument to the method. In the particular case of the code for the two-parameter method above (on lines 1 through 3), these types that must be the same must be the Character type (since the stream items are char types). If I have understood the compiler error correctly, in summary, it simply indicates that an identity argument of the wrong type (i.e., the String, rather than the Character, type) has been provided.

The three-parameter reduce() stream operation/method requires the following, when the method is invoked: The type of the first, identity argument passed to the method must be the same as the (single) type of the BinaryOperator lambda passed as the third, combiner argument to the method as well as the type of the first parameter and return type of the BiFunction lambda passed as the second, accumulator argument to the method. In the particular case of the code for the three-parameter method above (on lines 5 through 7), the type of these types that must be the same is the String type.

Since the stream items in the code shown are char types, I expect that the type, of the first parameter of the BiFunction lambda passed as the second, accumulator argument to the three-parameter method, should be Character and not String. However, it appears like the compiler is okay with the three-parameter method having the type of this first parameter of the BiFunction lambda being String, rather than Character. I am unable to see why the compiler is okay with this, even though I do understand why it would be okay with the (single) type of the BinaryOperator lambda passed as the third, combiner argument to the method to be String.

Since the stream items in the code shown are char types, I do understand the reason for the compiler action described in the point number 1 below, but I do not understand the reason for the compiler action described in the point number 2 below:

(1.) The compiler is okay with the two parameters, of the BinaryOperator lambda passed as the second, accumulator argument to the two-parameter method, being the Character type.

(2.) The compiler is okay with the first parameter, of the BiFunction lambda passed as the second, accumulator argument to the three-parameter method, being the String type (shouldn't this type be Character and not String?).

Could the reason, for the compiler action mentioned in the point number 2 above, be because the accumulator operates on intermediate reductions that would be strings, since these intermediate reductions would be strings that are produced/generated when the combiner concatenates the empty string identity with pairs of the char types in the stream shown in the code (I have noticed that applying the "+" operator to a pair of char types evaluates to an int type, but applying the operator to char types and a string, including an empty string, evaluates to a String type)?
 
Marshal
Posts: 26914
82
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I put your three-parameter code into Eclipse to see what it had to say. As you say, it compiles correctly and runs.

So I hovered the mouse over various parts of the reduce member's parameters and it interprets them as
The type Character in that interpretation is required because the reduce() method is a method of a Stream<Character>, and the type String is required because the first parameter of the method is a String constant.
 
Nyeng Gyang
Ranch Hand
Posts: 113
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks for taking the time to respond to my inquiry.

The following is the header of the reduce() method (this method header is provisioned in the Java documentation here):
My understanding of the reduce() method header above is as follows: (1) All the types U in the method header should be the same type, rather than different types, and that this lone/single type is String and (2) The type T in the method header is the Character type (since the stream items are char types), which means that the second parameter of the BiFunction lambda argument to the method should be of either the Character type or a superclass of the Character type (kindly correct me if this understanding is wrong, please; in any case, the information provided in your response to my inquiry is congruent with most of this understanding that I have).

Actually, the reason I asked whether the first parameter, of the BiFunction lambda passed as the accumulator argument to the three-parameter method, should be the Character, rather than the String, type is because I wrongly thought that the combiner operates on the stream items before the accumulator does the same. After thinking on and analyzing the issue further, I think the accumulator operates on the stream items before the combiner operates on them, as follows (kindly correct me if this analysis that I have conducted is wrong, please):

(1.) The String type of the first parameter of the BiFunction lambda accumulator argument to the method is obtained as follows: The empty string identity is concatenated with one char type in the stream to produce/generate a string. This string is produced/generated before it is passed as an argument to the lambda itself, resulting in the first and second parameters of this BiFunction lambda being String and Character, respectively.

(2.) The BiFunction lambda accumulator argument to the method takes String and Character arguments, as explained in the point number 1 above, and produces strings that are intermediate reductions and concatenated pairs of char items in the stream.

(3.) The BinaryOperator lambda combiner argument to the method then takes pairs of the strings, which the point number 2 above says are intermediate reductions that are returned by the BiFunction lambda, as arguments and concatenates them, producing a string for each pair of strings that the BinaryOperator lambda takes as arguments.

After conducting other, different analyses, the foregoing one above is the only one that is congruent with what I think the types U and T, in the reduce() method header above, should be.
 
Nyeng Gyang
Ranch Hand
Posts: 113
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Paul Clapham wrote:So I hovered the mouse over various parts of the reduce member's parameters and it interprets them as


Since the return type of a BiFunction does not have to be the same as the type of either of its two parameters, shouldn't Eclipse provide three, rather than only two, types inside the < and > symbols? Furthermore, if my analysis above is accurate and for the particular example of the code I provided in my initial post above, the third type that should be provided inside the < and > symbols would be the String type. What do you think?
 
Bartender
Posts: 4702
183
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Nyeng wrote:The three-parameter reduce() stream operation/method requires the following, when the method is invoked: The type of the first, identity argument passed to the method must be the same as the (single) type of the BinaryOperator lambda passed as the third, combiner argument to the method as well as the type of the first parameter and return type of the BiFunction lambda passed as the second, accumulator argument to the method. In the particular case of the code for the three-parameter method above (on lines 5 through 7), the type of these types that must be the same is the String type.

Since the stream items in the code shown are char types, I expect that the type, of the first parameter of the BiFunction lambda passed as the second, accumulator argument to the three-parameter method, should be Character and not String. However, it appears like the compiler is okay with the three-parameter method having the type of this first parameter of the BiFunction lambda being String, rather than Character. I am unable to see why the compiler is okay with this, even though I do understand why it would be okay with the (single) type of the BinaryOperator lambda passed as the third, combiner argument to the method to be String.



You go from characters to String using the reduce-method. The only way to do that with the reduce-method is by using the 3-parameter-version of that method. As you quoted, the API says:

So, what we have is that the first parameter specifies the new result type, and since this is a String, the result of the complete reduce will be a String. But we are dealing with a Stream of chars, so we have to specify how we go from chars to a String, and that is where the BiFunction steps in. Here it is a BiFunction<String, Character, String>, and in this case that method says to add the character from the Stream to the String that we started with. And finally: if you would be using a parallel Stream, then we will get a few Strings from this, and the third parameter specifies how to combine these Strings in order to get the final String as a result.

Compare this code:

Here we go from characters to Integer, and the whole difference is in the first argument: it is now a 0, while before it was a String.
 
Nyeng Gyang
Ranch Hand
Posts: 113
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Piet Souris wrote:You go from characters to String using the reduce-method.


After spending some time analyzing the behavior of the three-parameter reduce() method, I have discovered the following:

(1.) The combiner argument to the method is simply ignored (i.e., not applied to a stream) if the stream that the reduce operation is applied to is a serial stream; the combiner argument is only applied when the stream the operation is applied to is a parallel stream.

(2.) Each of all threads that perform the reduce operation starts by first applying the operator in the accumulator lambda to the identity and a stream item as operands.

(A.) When the reduce operation is performed on a serial stream (typically by a single thread), this single thread then continues to successively apply the operator in the accumulator lambda to the accumulated result and the next item in the stream as operands.

(B.) When the reduce operation is performed on a parallel stream (typically by multiple threads), the application of the operator in the accumulator lambda to the identity and a stream item as operands (by each of these multiple threads) produces/generates intermediate reductions. The operator in the combiner lambda is then successively applied to these intermediate reductions as operands, until a single/final reduction is then obtained.

In the particular case of the stream I provided, a single thread performing the reduction first concatenates the empty string identity with the first stream char item, producing/generating an accumulated reduction that is a string containing only the first char in the stream. This String accumulated reduction is then concatenated with the next char item in the stream and so on (I will save the explanation for how the reduction is computed for a parallel stream, since that is superfluous, will digress and may distract from the task of determining the types of the BiFucntion lambda accumulator argument to the reduce operation). This explanation also provides me the answer to why the BiFucntion lambda has a String and Character parameter.

In addition to the functional mapping explanation you provided in your response, the starting String identity and the subsequent String accumulated reductions (obtained from concatenating String and Character types) also explain why the BiFucntion lambda has a String and Character parameter. Appying this explanation to your example of the integer identity and a stream of char types goes thus: The accumulator ("+") operator evaluates the int identity and the first char stream item as operands to an int result and the combiner subsequently applies the combiner ("+") operator to the int type of the resulting intermediate reductions; in other words, the starting Integer identity and the subsequent Integer accumulated reductions (obtained from arithmetic adding of Integer and Character types) explain why the BiFucntion lambda in your example would have an Integer and a Character parameter (i.e., the BiFucntion in the example would be BiFunction<Integer, Character, Integer>).
 
You showed up just in time for the waffles! And this tiny ad:
Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop
https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
reply
    Bookmark Topic Watch Topic
  • New Topic