• 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
  • Ron McLeod
  • Liutauras Vilda
  • Jeanne Boyarsky
Sheriffs:
  • Junilu Lacar
  • Rob Spoor
  • Paul Clapham
Saloon Keepers:
  • Tim Holloway
  • Tim Moores
  • Jesse Silverman
  • Stephan van Hulst
  • Carey Brown
Bartenders:
  • Al Hobbs
  • Piet Souris
  • Frits Walraven

Understanding the Java API for Map.merge(key, value, BiFunction) ?

 
Ranch Hand
Posts: 327
6
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm trying to practice and understand the Java API and recently I ran across the below code as a better solution to an exercise I completed on codewars.com .   I'm not familiar with lines 11 and 12 so I'm trying to understand it.  I turned to the Java 8 API on it ( what came up in Google ) and I find the BiFunction part hard to understand.  https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#merge-K-V-java.util.function.BiFunction-


BiFunction<? super V, ? super V, ? extends V> remappingFunction

I think the first two "? super V" are for generic values ( specifically Lower Bounded Wildcard? ) and the code below I think is passing the key and value .
I think BiFunction is an Interface which is why a Lambda function can be used.
BUT when I click on the BiFunction Interface link I don't see a method called "remappingFunction" .  What is "remappingFunction" then?    How do I find information about that?

I hope my question makes sense.  




 
Saloon Keeper
Posts: 13498
305
  • Likes 3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Map.merge() takes three arguments:

  • The key for which to compute a new value.
  • The value to use in the computation of the new value.
  • A remapping function that is called when the map already contains an entry for the given key.

  • If there is no entry associated with the given key yet, merge() will create a new one and simply put the value you passed as the second argument.

    If there already is an entry associated with the given key, merge() will call the remapping function. The remapping function takes two arguments:

  • The existing value that is associated with the given key.
  • The new value that you passed to the merge() method.

  • The remapping function must return the value that will be associated with the key after the merge() method returns. You can use this function to quite literally merge the old and the new value.

    The merge call from your example does the following: If a character hasn't been encountered yet, it associates the value 1 with it. If a character has encountered before, it associates the value a + b with it, where a is the previous character count, and b is the value 1.

    The code is also a good example of why you should not use single letter identifiers, even for lambda parameters. The code would have been easier to understand if it read like this:

    Some people though, myself included, prefer to use method handles instead of lambda expressions:
     
    Sheriff
    Posts: 7113
    184
    Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    BiFunction<? super V, ? super V, ? extends V> remappingFunction


    The remappingFunction is just the name of the variable they happened to pick.  It's just like except that the name "foo" was made to be self-documenting.

    As far as the wildcard generic types, you can think of them as "producers use extends, consumers use super" or PECS.  Here is a really good Stack Overflow post.  The first three answers are all good.

    https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super
     
    Lisa Austin
    Ranch Hand
    Posts: 327
    6
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Knute Snortum wrote:

    BiFunction<? super V, ? super V, ? extends V> remappingFunction


    The remappingFunction is just the name of the variable they happened to pick.  It's just like except that the name "foo" was made to be self-documenting.

    As far as the wildcard generic types, you can think of them as "producers use extends, consumers use super" or PECS.  Here is a really good Stack Overflow post.  The first three answers are all good.

    https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super



    Just a variable name?  Okay  Thank you.  I appreciate that .  Now that you told me that it makes sense since it's the arguments which the merge method takes .    Also Thank you for the info about the Wildcard generic types.  I'm still trying to grasp that as well.
     
    Lisa Austin
    Ranch Hand
    Posts: 327
    6
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Stephan van Hulst wrote:Map.merge() takes three arguments:


    The code is also a good example of why you should not use single letter identifiers, even for lambda parameters. The code would have been easier to understand if it read like this:

    Some people though, myself included, prefer to use method handles instead of lambda expressions:



    Thank you for all this and I agree about the variable names.    Also thanks for explaining the following


    I saw this in the API and most examples I was looking up used this as well and I was going to look up what this was.  I hadn't ran across "method handles" as of yet so Thank you!
     
    Sheriff
    Posts: 16767
    281
    Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Lisa Austin wrote:I hadn't ran across "method handles" as of yet so Thank you!


    Officially, they're called "method references" but "handles" works, too.
     
    Knute Snortum
    Sheriff
    Posts: 7113
    184
    Eclipse IDE Postgres Database VI Editor Chrome Java Ubuntu
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Lisa Austin, just a quick comment about WhenToQuote (that's a link).  Don't quote the entire proceeding message.  Only quote enough to show context.  Usually you just want to press the Reply button.
     
    Lisa Austin
    Ranch Hand
    Posts: 327
    6
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Knute Snortum wrote:Lisa Austin, just a quick comment about WhenToQuote (that's a link).  



    Will keep this in mind.
     
    Lisa Austin
    Ranch Hand
    Posts: 327
    6
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Junilu Lacar wrote:
    Officially, they're called "method references" but "handles" works, too.



    Thank you for that!
     
    Lisa Austin
    Ranch Hand
    Posts: 327
    6
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Stephan van Hulst wrote:

    If there already is an entry associated with the given key, merge() will call the remapping function. The remapping function takes two arguments:

  • The existing value that is associated with the given key.
  • The new value that you passed to the merge() method.

  • The remapping function must return the value that will be associated with the key after the merge() method returns. You can use this function to quite literally merge the old and the new value.



    I have more questions about understanding the API and this merge() method.    

    I understand what is being said in this quote so I'm not asking about how to use it.   In the API it shows the BiFunction as BiFunction<? super V, ? super V, ? extends V> remappingFunction  .
    Since the remapping function takes two arguments am I correct to say the lambda is calling the BiFunction's apply() method ?

    And Questions about  <? super V, ? super V, ? extends V>  
    I want to verify what I think I understand about reading this.
    I think these are Generic types?  
    The first two are consumer types so we are giving the BiFunction something which in this case is the key and value ?  
    Is the third type ( ? extends V) representing what the BiFunction returns back?


    Last question.
    How does the Method Handles / Reference in the code example below replace BiFunction<? super V, ? super V, ? extends V> remappingFunction ?    


    It seems like the class Integer is being used now and not BiFunction?  But doesn't the Merge method require BiFunction?
     
    Stephan van Hulst
    Saloon Keeper
    Posts: 13498
    305
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Lisa Austin wrote:Since the remapping function takes two arguments am I correct to say the lambda is calling the BiFunction's apply() method ?


    No. The lambda expression returns an object that implements BiFunction. We can make it more explicit like this:

    You can view a lambda expression as a really short way of declaring a class that implements the functional interface and creates a new instance of it. If you are familiar with anonymous classes, using the lambda expression above is very similar to using:


    I think these are Generic types?


    No, they are called generic type arguments. BiFunction is a generic type. It declares generic type parameters T, U and R. And the merge() method's method signature assigns the generic type arguments ? super V, ? super V and ? extends V to the parameters T, U and R respectively.

    The first two are consumer types so we are giving the BiFunction something which in this case is the key and value ?


    It just means that if you are calling merge() on a Map<Character, Integer>, you can pass it an object of type BiFunction<Integer, Integer, Integer>, or a BiFunction<Number, Number, Integer>, or even a BiFunction<Integer, Object, MySpecialInteger>, if you could extend Integer. The wildcard (the question mark) just loosens the type bound so that you can assign superclasses for the first two type parameters (T and U) and subclasses for the last type parameter (R).

    And the arguments to the BiFunction are not a key and a value. They are the old value and the new value.

    Is the third type ( ? extends V) representing what the BiFunction returns back?


    Yes. When declaring parameters of a generic type, it's a good idea to use upper bounds (? extends) for return types, and lower bounds (? super) for types that are used in function parameter lists. You can clearly see this in the BiFunction interface. T and U are used for the parameters of the apply() method, and R is used as the return type. That's why merge() uses ? super V for T and U, and it uses ? extends V for R.

    How does the Method Handles / Reference in the code example below replace BiFunction<? super V, ? super V, ? extends V> remappingFunction ?


    A method reference can be seen as a shorthand for a lambda expression. Integer::sum really means the same as (oldCount, increment) -> Integer.sum(oldCount, increment). It's easier though if you forget all the technical details, and simply reason like this:

    BiFunction<Integer, Integer, Integer> represents the type of a function that takes two integers and returns an integer result. Integer.sum() happens to be a function that takes two integers and returns an integer result, so it's compatible with BiFunction<Integer, Integer, Integer>, which is why you can use a method reference instead of a lambda expression.

    It seems like the class Integer is being used now and not BiFunction?  But doesn't the Merge method require BiFunction?


    You're not using the Integer class. You're using the sum() function, and the sum() function is compatible with BiFunction<Integer, Integer, Integer>.
     
    Lisa Austin
    Ranch Hand
    Posts: 327
    6
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Okay I think I get it.  Thank you for answering each of my questions , breaking down the details.  
     
    Consider Paul's rocket mass heater.
    reply
      Bookmark Topic Watch Topic
    • New Topic