Identity for BinaryOperator
Identity for BinaryOperator
I see in Java8 in UnaryOperator Interface following piece of code which does nothing on parameter and returns same value.
static <T> UnaryOperator<T> identity()
return t -> t;
Is there anything for BinaryOperator which accepts two parameters of samekind and returns one value
static <T> BinaryOperator<T> identity()
return (t,t) -> t;
why I am asking this question is for below requirement,
List<String> list = Arrays.asList("Abcd","Abcd");
Map<String,Integer> map = list.stream().collect(Collectors.toMap(str->str,
str->(Integer)str.length(),(t1,t2)->t1));
System.out.println(map.size());
in above code I don't want to do anything for two values of same key, I just wanted return one value, because in my case for sure values will be same.
As am not using t2 value Sonar throwing error, So I am finding out is there any thing like UnaryOperator.identity() for BinaryOpertor also in java8
.Distinct
.GroupBy
The problem is not in your code, but in the Sonar alert instead. Just turn it off.
– Federico Peralta Schaffner
Sep 12 '18 at 13:44
Some functional languages only have unary functions. In these languages, a function accepting a tuple is equivalent to a binary function in Java. Identity for such a function is easy when you think of it in this way: if a tuple is input, it must also be the output.
– user633183
Sep 12 '18 at 13:45
3 Answers
3
Your question doesn't really make sense. If you were to paste your proposed BinaryOperator.identity
method into an IDE, you would immediately see that it would complain that the identifier t
is declared twice.
BinaryOperator.identity
t
To fix this, we need a different identifier for each parameter:
return (t, u) -> t;
Now we can clearly see that this is not an identity function. It's a method which takes two arguments and returns the first one. Therefore the best name for this would be something like getFirst
.
getFirst
To answer your question about whether there's anything like this in the JDK: no. Using an identity function is a common use case, so defining a method for that is useful. Arbitrarily returning the first argument of two is not a common use case, and it's not useful to have a method to do that.
Well, I agree to what you've stated here, except for the last part:
Arbitrarily returning the first argument of two is not a common use case, and it's not useful to have a method to do that
. In fact it is quite useful, as OP is showing here. Another use case is when implementing collectors and you need to provide a combiner function that you know will never be used (because i.e. the stream is sequential). Besides, BinaryOperator
does provide maxBy(Comparator)
static method (and also minBy
), so it's valid to wonder why there's no i.e. first
or second
methods...– Federico Peralta Schaffner
Sep 12 '18 at 15:41
Arbitrarily returning the first argument of two is not a common use case, and it's not useful to have a method to do that
BinaryOperator
maxBy(Comparator)
minBy
first
second
@FedericoPeraltaSchaffner "common" is kind of subjective. I've never needed that, and I work with the JDK on a daily basis. By contrast, I've needed
Function.identity
probably 100s of times. The JDK is supposed to be a collection of broadly useful tools. It's not designed to contain absolutely everything a developer might ever need. If you add a getFirst
, people will wonder where getSecond
is. If you add both of those for BinaryOperator
, why not add similar ones for the other 42 functional interfaces? Boom - you've now got ~100 basically useless methods cluttering the API.– Michael
Sep 12 '18 at 15:53
Function.identity
getFirst
getSecond
BinaryOperator
I absolutely agree with you, I was playing the role of the devil's advocate a little bit... The problem with the question is not that there doesn't exist a method to choose the first or second element, but that Sonar is throwing an alert, when it shouldn't...
– Federico Peralta Schaffner
Sep 12 '18 at 15:56
T
means they have the same types, not the same values, that is not an identity per-se.
T
It just means that BinaryOperator
will be used for the same types, but providing an identity
for different values... this somehow sounds like foldLeft
or foldRight
or foldLeftIdentity/foldRightIdentity
, which java does not have.
BinaryOperator
identity
foldLeft
foldRight
foldLeftIdentity/foldRightIdentity
what's your thought on the implementation of
any
for returning any value as in the merger?– nullpointer
Sep 12 '18 at 13:45
any
@nullpointer :) that is all I can say. probably
Math.random
is too expensive here, I would go for something cheaper like long nt = System.nanoTime(); ((nt >>> 32) ^ nt) > 0 ? ... : ...
but I've seen this somewhere else already :) (of course the cheapest and fastest random would have to be taken here)– Eugene
Sep 12 '18 at 14:05
Math.random
long nt = System.nanoTime(); ((nt >>> 32) ^ nt) > 0 ? ... : ...
Your code seemingly can be improved as
List<String> list = Arrays.asList("Abcd", "Abcd");
Map<String, Integer> map = list.stream()
.collect(Collectors.toMap(Function.identity(), String::length, (a, b) -> a));
System.out.println(map.size());
Or possibly for your use case I don't want to do anything for two values of same key, I just wanted return one value, you may just choose to randomly return any value in using an implementation as following:
private static <T> BinaryOperator<T> any()
return Math.random() < 0.5 ? ((x, y) -> x) : ((x, y) -> y);
and then in your code use it as
Map<String, Integer> map = list.stream()
.collect(Collectors.toMap(Function.identity(), String::length, any()));
Thanks to the suggestions from Holger, Eugene, and Federico, there are other efficient implementations of the any
method that can actually involve using :
any
private static <T> BinaryOperator<T> any()
// suggested by Holger
return ThreadLocalRandom.current().nextBoolean() ? ((x, y) -> x) : ((x, y) -> y);
// suggested by Eugene
long nt = System.nanoTime();
((nt >>> 32) ^ nt) > 0 ? ((x, y) -> x) : ((x, y) -> y);
that btw is a very interesting merger :)
– Eugene
Sep 12 '18 at 13:19
I guess you are right, it just looks weird to me.
– Eugene
Sep 12 '18 at 13:24
You know that your random implementation won't pick a random argument each time, but will choose one implementation when called, and then will return the same argument all times. btw I didn't downvote
– Federico Peralta Schaffner
Sep 12 '18 at 13:48
I remember I asked something about that here and Stuart Marks kindly answered... :D
– Federico Peralta Schaffner
Sep 12 '18 at 14:15
I’d use
ThreadLocalRandom.current().nextBoolean()
instead of Math.random() < 0.5
. An alternative would be (a,b) -> assert Objects.equals(a, b); return a;
– Holger
Sep 12 '18 at 17:30
ThreadLocalRandom.current().nextBoolean()
Math.random() < 0.5
(a,b) -> assert Objects.equals(a, b); return a;
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
An identity, by definition, returns its input without any modification. If the input is two values, its output must also be two values. What you want is not an identity, but some sort of distinct or group operation before processing the values. (I don't speak modern Java, but in C# that would literally be
.Distinct
and.GroupBy
.)– Jeroen Mostert
Sep 12 '18 at 13:17