Why does Map.compute() take a BiFunction - java

I don't understand why Map.compute() and Map.computeIfPresent() take BiFunction parameters as well as Map.computeIfAbsent() a Function:
V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
I'd expect an ordinary Function<? super V, ? extends V>, mapping the old value to a new value, resp. a Supplier<? extends V> for the new value. The caller already has the key (first argument) so the function or the supplier can already make use of it. All examples I found don't use the key. The reasons that come to my mind:
the key must be (effectively) final -- that's easy to manage
there are some fancy easy-to-use method references
But I don't believe these are viable reasons for this design. Do you have any ideas?

You may see computeIfPresent as the single-entry pendant of replaceAll whereas the latter requires the key as a parameter, but it’s natural to support the same function as input to both operations and the API is consistent here: it always provides the key as parameter to the function.
Generally, providing the key raises the reusability of existing functions, be it method references or ordinary class implementations (i.e. non-lambda) of the BiFunction interface. But this reusability may also affect the performance of lambda expressions given the existing JRE implementation:
As described here, lambda expressions capturing values from the surrounding context may end up in individual instances for each capturing process, whereas lambda expressions only using their parameters (non-capturing lambdas) will end up as a singleton instance. In contrast, having an otherwise unused parameter has no performance impact. So receiving the key as a parameter is preferable for that reason as well.

Related

Why Comparable and Comparator are consumers in PECS wildcard types in Java

In Effective Java, in the item "Use bounded wildcards to increase API flexibility", when talking about the usage of PECS (producer-extends, consumer-super), the author mentioned that:
Comparables are always consumers, so you should generally use Comparable<? super T> in preference to Comparable. The same is true of comparators; therefore, you should generally use Comparator<? super T> in preference to Comparator.
It is not clear to me why Comparables and Comparators are considered consumers.
In one of the topic discussing PECS, What is PECS (Producer Extends Consumer Super)?, the consumer is usually referring to a Collection as a parameter for some generic method.
While here Comparable is just an interface.
Can anyone share some insights? Thanks!
A nice analogy can be drawn to the interfaces Consumer<T> and Supplier<T> (with Supplier being analogous to Producer). A Consumer<T> is a function that takes in a T, while a Supplier<T> is a function that returns a T. Notice that we are talking about method signatures and return type, we say nothing about the semantics of the method. This is a core property of PECS: it is independent of the semantics and can be determined solely on the signature and return type of the methods used.
Looking at Comparable<T> and Comparator<T>, we find that both have methods (int compareTo(T) and int compare(T, T)) that take in, i.e. consume, T's.
For the collections, we have to look on how we use the collection, i.e. if we are using producer- or consumer-methods:
If we retrieve data from the collection (T get(int), iterator, ...), the list produces values for us and we use ? extends T.
If we use the collection to store data, (i.e. we call add(T), addAll(Collection<T>), contains(T), ...), we call consuming methods, thus the method is a consumer of our data and we use ? super T.
If we use a collection to both store and retrieve values, the collection acts as a consumer and producer at the same time, thus we have to use the precise T, neither using ... extends ... nor ... super ....
„…It is not clear to me why Comparables and Comparators are considered consumers.…“
Anytime a method member of a generic class C<T>, takes in (i.e. „consumes“) an argument that is of type T, then that method is a consumer of Ts.
So Comparator<T>.compareTo(T o) is said to be a „consumer“ of the o object of the type represented by the type variable T.
When we say "Comparable is a consumer", we actually mean "the method of the interface is a consumer". This is an answer to your doubt that "Comparable is just an interface."
See its signature: https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/Comparable.html#compareTo(T)
int compareTo​(T o)
It consumes the generic T, it does not produce any generic object.

What does it mean in groupingBy in the Java API and how to use it?

I'm looking at the Java API regarding Collectors.
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#groupingBy-java.util.function.Function-java.util.function.Supplier-java.util.stream.Collector-
public static <T,K,D,A,M extends Map<K,D>> Collector<T,?,M> groupingBy(Function<? super T,? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T,A,D> downstream)
Type Parameters:
T - the type of the input elements
K - the type of the keys
A - the intermediate accumulation type of the downstream collector
D - the result type of the downstream reduction
M - the type of the resulting Map
Parameters:
classifier - a classifier function mapping input elements to keys
downstream - a Collector implementing the downstream reduction
mapFactory - a function which, when called, produces a new empty Map
of the desired type
Returns:
a Collector implementing the cascaded group-by operation
From the first line of code, I understand that it is a public static method but what does it mean by <T, K, D, A, M extends Map<K, D>>?
And then from Collector<T, ?, M>, I can understand that it returns a Collector but what is the meaning of the ?.
Additionally, I went onto the page of one of the parameters, Supplier and since it's a functional interface it has only one abstract method called get but I simply do not know how to use it at all. And the book which I'm going through teaches us to use TreeMap::new.
My confusion here is how come the parameter requires something that implements the Supplier interface but yet we are able to use the method reference instead?
Thanks in advance!
<T, K, D, A, M extends Map<K, D>> means that this method uses 5 generic type parameters - T, K, D, A and M where M must implement Map<K, D>.
? denotes a wild card i.e. you don't care what type that is. Note that this is different from adding another generic type parameter. If it were another generic type parameter, say, U, it can be referred later in the code. You care about it.
Supplier represents a method that accepts no parameters and returns a value of some type. TreeMap::new takes no parameters and it gives you a TreeMap, so this method reference can be used here.
<T,K,D,A,M extends Map<K,D>> - these are generic parameters for this function, that are used as this functions' parameters and result generic parameters. So, your parameters have to be:
Function<? super T,? extends K> classifier - function that takes T and returns K
Supplier<M> mapFactory - supplier creates M
Collector<? super T,A,D> downstream - collector with T, A and D parameters (look further explanation and documentation for details)
And also this function produces Collector<T,?,M> - here goes T and M. All these parameters should be specified either explicitly or implicitly.
About collector parameters, you can just go to Collector documentation page and see, that second generic parameter for Collector class is
the mutable accumulation type of the reduction operation (often hidden as an implementation detail)
Usually, you don't care about this mutable accumulation type, because in the end you need only result, which, in this case, is M.
About supplier - i would also advise to go to documentation page. It's an interface with one method T get(). And this TreeMap::new can be represented as () -> new TreeMap<>(). If that is also unclear, then you probably need to read some tutorials about anonymous classes, functional interfaces and lambda functions (and method references) in general.

Inconsistency in Java 8 method signatures

Java 8 has given us new methods with really long signatures like this:
static <T,K,U,M extends Map<K,U>> Collector<T,?,M> toMap(
Function<? super T,? extends K> keyMapper,
Function<? super T,? extends U> valueMapper,
BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
What I find odd about this is that wildcards have been used to ensure that the first two parameters are as general as possible, yet the third parameter is just a BinaryOperator<U>. If they'd been consistent, surely it would be a BiFunction<? super U,? super U,? extends U>?. Am I missing something? Is there a good reason for this, or did they just want to avoid making an already horrendous signature even worse?
Edit
I understand PECS, and I understand the principle that mergeFunction should be thought of as a way of taking two Us and getting back a U. However it would be useful to be able to have an object that could be reused in many different ways. For example:
static final BiFunction<Number, Number, Double>
MULTIPLY_DOUBLES = (a, b) -> a.doubleValue() * b.doubleValue();
Obviously this is not a BinaryOperator<Double>, but it could be treated as one. It would be great if you could use MULTIPLY_DOUBLES as both a BiFunction<Number, Number, Double> and a BinaryOperator<Double>, depending on the context. In particular, you could simply pass MULTIPLY_DOUBLES to indicate that you want a load of doubles to be reduced using multiplication. However the signature for toMap (and other new methods in Java 8) does not allow for this kind of flexibility.
You are right in that the functional signature of the merge operation (the same applies to reduce) does not require an interface like BinaryOperator.
This can not only be illustrated by the fact that the mergeFunction of the toMap collector will end up at Map.merge which accepts a BiFunction<? super V,? super V,? extends V>; you can also convert such a BiFunction to the required BinaryOperator:
BiFunction<Number, Number, Double>
MULTIPLY_DOUBLES = (a, b) -> a.doubleValue() * b.doubleValue();
Stream<Double> s = Stream.of(42.0, 0.815);
Optional<Double> n=s.reduce(MULTIPLY_DOUBLES::apply);
or full generic:
public static <T> Optional<T> reduce(
Stream<T> s, BiFunction<? super T, ? super T, ? extends T> f) {
return s.reduce(f::apply);
}
The most likely reason for creating BinaryOperator and UnaryOperator is to have symmetry with the primitive type versions of these functions which don’t have such a super interface.
In this regard, the methods are consistent
Stream.reduce(BinaryOperator<T>)
IntStream.reduce(IntBinaryOperator)
DoubleStream.reduce(DoubleBinaryOperator)
LongStream.reduce(LongBinaryOperator)
or
Arrays.parallelPrefix(T[] array, BinaryOperator<T> op)
Arrays.parallelPrefix(int[] array, IntBinaryOperator op)
Arrays.parallelPrefix(double[] array, DoubleBinaryOperator op)
Arrays.parallelPrefix(long[] array, LongBinaryOperator op)
the BinaryOperator<U> mergeFunction needs to take Us from an input source and put them into another consumer.
Due to the Get and Put Principle, the type has to be exactly the same. No wild cards.
The get-put principle, as stated in Naftalin and Wadler's fine book on generics, Java Generics and Collections says:
Use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don't use a wildcard when you do both.
Therefore it can't beBiFunction<? super U,? super U,? extends U> mergefunction because we are doing get and put operations. Therefore the input and result type must be identical.
see these other links for more about Get and Put:
Explanation of the get-put principle (SO question)
http://www.ibm.com/developerworks/library/j-jtp07018/
EDIT
As Gab points out, the Get and Put principle is also known by the Acronym PECS for "Producer Extends Consumer Super"
What is PECS (Producer Extends Consumer Super)?
Looking at the implementation of the Collectors#toMap in question, one can see that the operator is passed through to some other methods, but eventually only arrives as the remappingFunction in various forms of Map#merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction).
So using BiFunction<? super V, ? super V, ? extends V> instead of BinaryOperator<V> would indeed work here, without causing any problem. But not only here: The BinaryOperator is only a specialization of BiFunction for the case that the operands and the result are all of the same type. So there are many places where one could allow passing in a BiFunction<? super V, ? super V, ? extends V> instead of a BinaryOperator<V> (or, more obviously: One could always use a BiFunction<V, V, V> instead...)
So up to this point, there seems to be no technical reason why they chose to only support a BinaryOperator<U>.
There was already speculation about possible non-technical reasons. For example, limiting the complexity of the method signature. I'm not sure whether this applies here, but it could, indeed, be a trade-off between the complexity of the method and the intended application cases: The concept of a "binary operator" is easily comprehensible, for example, by drawing analogies to a simple addition or the union of two sets - or maps, in this case.
A possible not-so-obvious technical reason could be that there should be the possibility to provide implementations of this method that internally would not be able to cope with the BiFunction. But considering that the BinaryOperator is only a specialization, it's hard to imagine what such an implementation should look like.

Sorting Map in descending order

I've actually got this working from this example:
static <K,V extends Comparable<? super V>>
List<Entry<K, V>> entriesSortedByValues(Map<K,V> map) {
List<Entry<K,V>> sortedEntries = new ArrayList<Entry<K,V>>(map.entrySet());
Collections.sort(sortedEntries,
new Comparator<Entry<K,V>>() {
#Override
public int compare(Entry<K,V> e1, Entry<K,V> e2) {
return e2.getValue().compareTo(e1.getValue());
}
}
);
return sortedEntries;
}
I found it on this site; here.
Now, I have implemented this code and it does work. I understand what's happening, for the most part. The only part of the code I don't fully understand is within the method header, specifically:
static <K,V extends Comparable<? super V>>
If anybody could explain this part of the code to me, I'd be very grateful. Specifically why I should use super in this context.
I'm fairly new to Java so trying to get a good understanding of using different techniques rather than just throwing them into my code without really understanding what's happening.
Note: I would have commented on the original answer but I can only leave comments if I have a reputation over 50. I hope I'm not breaking any rules by posting like this!
This line is making the method generic. You can have a generic type in either the class header or in the method declaration. In this case, the method declaration.
static <K,V extends Comparable<? super V>>
declares a generic type K that is unbounded. This means it can be any subclass of type Object. Then it declares a second type, V, that MUST implement the Comparable interface. The Comparable implementation is also typed, and the method adds a bound to that, stating that the Comparable class should be able to compare to anything of a supertype of generic type V.
Extra Reading
Have a look into Java Generics
The idea is that V is comparable to at least other Vs, but may be comparable to other types as well -- that is, comparable to anything of a supertype of V.

generic methods and wildcards

What are the differences between the following three signatures?
static <T> void foo(List<T>, Comparator<? super T>);
static <T> void bar(List<? extends T>, Comparator<T> );
static <T> void baz(List<? extends T>, Comparator<? super T>);
I know what extends and super mean in Generics. My question is whether there is a difference between foo, bar and baz. Should I make one of the parameters invariant and the other one variant in the appropriate direction, or should I make both of them variant? Does it make a difference?
PECS - Producer extends, consumer super.
To explain this "rule":
extends means the genericized object produces elements of the type. When it's a collection, it means you can only take elements from the collection, but not put them in. The comparator
super means the object consumes objects of the selected type. So you can add to the collection, but not read from it.
the lack of extends and super means you can do both for the exact type specified.
Regarding the Comparator, I don't think it makes any difference. Normally, it would be <? super T> because you the comparator consumes objects, but in all three cases you can safely invoke Collections.sort(list, comparator); (the signature of which is <? super T>)
The only difference is whether T represents the type parameter of the List, the Comparator, or something in between.
As far as the caller is concerned, the three method signatures are equivalent, i.e. whenever one of them can be used, the others can be used as well.
For the method implementation foo is probably most convenient, as it allows modifying the list without needing an additional capture conversion, which would require delegating to a helper method.
I believe that ? extends T means that the List may be generic of any type that is derived from T, whereas List<T> can only be a List of T and not any of its derived classes.

Categories

Resources