10

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

Naman
  • 27,789
  • 26
  • 218
  • 353
Karunakar Reddy L
  • 342
  • 1
  • 4
  • 21
  • 5
    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
  • 2
    The problem is not in your code, but in the Sonar alert instead. Just turn it off. – fps Sep 12 '18 at 13:44
  • 1
    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. – Mulan Sep 12 '18 at 13:45

3 Answers3

7

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.

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.

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.

Michael
  • 41,989
  • 11
  • 82
  • 128
  • 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... – fps Sep 12 '18 at 15:41
  • @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
  • 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... – fps Sep 12 '18 at 15:56
4

T means they have the same types, not the same values, that is not an identity per-se.

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.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • what's your thought on the [implementation of `any`](https://stackoverflow.com/a/52296202/1746118) for returning any value as in the merger? – Naman Sep 12 '18 at 13:45
  • 1
    @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
4

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 :

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);
}
Naman
  • 27,789
  • 26
  • 218
  • 353
  • that btw is a *very* interesting merger :) – Eugene Sep 12 '18 at 13:19
  • @Eugene I guess I must mention as per the question... *I don't want to do anything for two values of same key, I just wanted return one value* – Naman Sep 12 '18 at 13:21
  • @Eugene I do second the thought of the `foldxx` in your answer though. Seems like an interesting implementation to achieve. Let me give it a try if possible. – Naman Sep 12 '18 at 13:28
  • @nullpointer thank you for improving my code , still It can be improved List list = Arrays.asList("Abcd", "Abcd"); Map map = list.stream() .collect(Collectors.toMap(Function.identity(), String::length, (a, b) -> a)); System.out.println(map.size()); Please correct me If I am wrong. – Karunakar Reddy L Sep 12 '18 at 13:34
  • @KarunakarReddyL correct, updated with that as well. – Naman Sep 12 '18 at 13:37
  • 3
    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 – fps Sep 12 '18 at 13:48
  • @FedericoPeraltaSchaffner thanks for pointing that out, tried out another interesting alternate... related to downvote, no comments unless someone helps me understand what I can improve(after the person downvotes) :) – Naman Sep 12 '18 at 13:58
  • @Eugene Really? Don't remember the code now, I looked at it some time ago... All I know is that it randomizes the elements based on some seed that is initialized at JVM launch... – fps Sep 12 '18 at 14:13
  • @FedericoPeraltaSchaffner where do you think I got that code from? :) – Eugene Sep 12 '18 at 14:14
  • 1
    I remember I asked something about that here and Stuart Marks kindly answered... :D – fps Sep 12 '18 at 14:15
  • @FedericoPeraltaSchaffner [might be this one](https://stackoverflow.com/questions/45352920/immutablecollections-setn-implementation-detail) – Eugene Sep 12 '18 at 14:17
  • @nullpointer btw, you do understand you have not touched the question *itself* at all, right? – Eugene Sep 12 '18 at 14:25
  • @Eugene I've found [my question and Stuart's answer](https://stackoverflow.com/q/45222328/1876620) (it is linked from his answer to your question) – fps Sep 12 '18 at 15:27
  • @Eugene I do. Just posted the suggestion to improve the existing code and the part of `any` implementation for returning either of the values involved in the binary operator...since `identity` for something with two operands doesn't make sense. – Naman Sep 12 '18 at 15:37
  • 3
    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
  • @Holger Would the assumption be safe that `ThreadLocalRandom` is again here to maximize the efficiency, as also pointed out by Eugen in the comments of his answer? – Naman Sep 12 '18 at 18:26
  • @Holger Also, related to the code shared, could it not boil down to `return (a, b) -> Objects.equals(a, b) ? a : b;`? – Naman Sep 12 '18 at 18:29
  • Yes, `ThreadLocalRandom` is about efficiency. The method `Math.random()` uses a thread safe `java.util.Random` instance under the hood and since it is shared (and the method is globally visible), there is no chance of eliminating this overhead. The `assert Objects.equals(a, b);` statement is specifically to allow a fail-fast (if assertions are enable) behavior, if the assumption that both objects are equal does not. That’s different to just returning one of them, as then, it depends on the subsequent code whether it makes a difference. Still not bad and sometimes, equality is not required. – Holger Sep 13 '18 at 06:28
  • *(if assertions are enable)* was thinking over this for some time yesterday after reading your comment... and figured out that's not always practiced though..yet the statement to what I could infer from the OP was even if they are not equal, choose *any* of them...that's when I actually thought of writing the any implementation. – Naman Sep 13 '18 at 07:07