38

Why should I use Function.identity() when it returns the same thing which it receives without doing anything using the input or modifying the input in some way?

Apple apple = new Apple(10, "green");
Function<Apple, Apple> identity = Function.identity();
identity.apply(apple);

There must be some practical usage of this which I am not able to figure out.

Boann
  • 48,794
  • 16
  • 117
  • 146
arunveersingh
  • 391
  • 3
  • 11

3 Answers3

37

The intended usage is when you're using a method that accepts a Function to map something, and you need to map the input directly to the output of the function (the 'identity' function).

As a very simple example, mapping a list of persons to a map from name to person:

import static java.util.function.Function.identity

// [...]

List<Person> persons = ...
Map<String, Person> = persons.stream()
        .collect(Collectors.toMap(Person::name, identity()))

The identity() function is just for convenience and readability. As Peter indicates in his answer, you could just use t -> t, but personally I think that using identity() communicates intent better as it leaves no room for interpretation like wondering if the original author forgot to do a transformation in that lambda. I admit though that is highly subjective, and assumes the reader knows what identity() does.

Possibly it may have some additional advantages in terms of memory as it reuses a single lambda definition, instead of having a specific lambda definition for this call. I think that impact is probably negligible in most cases.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
15

You can use it for a frequency count for example.

public static <T> Map<T, Long> frequencyCount(Collection<T> words) {
    return words.stream()
            .collect(Collectors.groupingBy(Function.identity(),
                    Collectors.counting());
}

In this case, you are saying the key to group by is the element in the collection (without transforming it).

Personally, I find this briefer

import static java.util.stream.Collectors.*;

public static Map<String, Long> frequencyCount(Collection<String> words) {
    return words.stream()
            .collect(groupingBy(t -> t,
                    counting());
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • @Eugene The implementation of `identity()` is `return t -> t` – Mark Rotteveel Sep 27 '18 at 08:43
  • 4
    @MarkRotteveel right. what I meant is [this](https://stackoverflow.com/a/28041480/1059372) – Eugene Sep 27 '18 at 08:45
  • If I had to implement a compiler for lambdas, I would make the lambda a synthetic, private static field (using an anonymous class) of the class declaring it unless it references variables which value will only be known at runtime. I'm not sure the answer you linked is 100% correct for cases where the lambda is self-contained. – Dici Sep 27 '18 at 09:02
  • @Dici that was btw a legal strategy that they initially wanted to go with (there are talks from Brian Goetz about this) - they choose not to. they choose to go with `invokedynamic` and generate the classes at runtime – Eugene Sep 27 '18 at 09:27
  • @Dici it is 100% correct, under the current implementation at least, very easy to prove... – Eugene Sep 27 '18 at 09:31
  • @Dici the current implementation strategy, as described [here](https://stackoverflow.com/a/27524543/2711488) and [here](https://stackoverflow.com/a/23991339/2711488) is at least on par with such a static field approach, usually even better as it is settling on the single transition from uninitialized to initialized of an `invokedynamic` instruction, which provides thread safe lazy initialization without post-construction overhead. But unlike your field approach, the `invokedynamic` solution allows future improvements without the need to recompile the code. – Holger Sep 28 '18 at 13:07
  • @Holger mm ok interesting – Dici Sep 28 '18 at 13:57
4

Suppose you have a List<String> strings = List.of("abc", "de") and you want to generate a Map where Key is value form that List and Value is it's length:

 Map<String, Integer> map = strings.stream() 
        .collect(Collectors.toMap(Function.identity(), String::length)) 

Generally some people see Function.identity() a little less readable than t -> t for example, but as explained here this is a bit different.

Eugene
  • 117,005
  • 15
  • 201
  • 306