ZetCode

Java Function.identity Method

Last modified: April 16, 2025

The Function.identity method is a static utility in Java's java.util.function.Function interface. It returns a function that always returns its input argument unchanged. This is useful in functional programming scenarios where a function is required but no transformation is needed.

Identity functions are particularly valuable when working with Java streams and collectors. They serve as placeholders when an operation requires a function parameter but you want to preserve the original values. The method was introduced in Java 8 as part of the functional programming enhancements.

Function.identity Basics

The identity method is defined as a static method in the Function interface. It returns a function that implements the identity operation - output equals input. The method signature is simple and straightforward.

static <T> Function<T, T> identity() {
    return t -> t;
}

The implementation simply returns a lambda expression that takes an input and returns it unchanged. The generic type parameter ensures type safety while maintaining flexibility. This makes it usable in various contexts.

Simple Identity Function Example

This basic example demonstrates how to create and use an identity function. We'll show both the direct usage and how it compares to a custom lambda that does the same thing.

Main.java
package com.zetcode;

import java.util.function.Function;

public class Main {

    public static void main(String[] args) {

        // Create identity function
        Function<String, String> identity = Function.identity();
        
        // Apply the function
        String input = "Hello, World!";
        String output = identity.apply(input);
        
        System.out.println("Input: " + input);
        System.out.println("Output: " + output);
        System.out.println("Same object? " + (input == output));
        
        // Equivalent lambda expression
        Function<String, String> customIdentity = s -> s;
        System.out.println("Custom identity: " + customIdentity.apply("Test"));
    }
}

This example shows that the identity function returns exactly what it receives. The output demonstrates that it's the same object reference. The custom lambda version shows what identity essentially does under the hood.

Identity in Stream Operations

A common use case for Function.identity is in stream operations, particularly with collectors. When you need to use values as keys in a map without transformation, identity is the perfect choice.

Main.java
package com.zetcode;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

    public static void main(String[] args) {

        // Create a stream of strings
        Stream<String> words = Stream.of("apple", "banana", "cherry");
        
        // Use identity in toMap collector
        Map<String, Integer> wordLengths = words.collect(
            Collectors.toMap(
                Function.identity(),  // Use the word itself as key
                String::length       // Use word length as value
            )
        );
        
        System.out.println("Word lengths: " + wordLengths);
        
        // Alternative without identity (more verbose)
        List<String> fruits = List.of("orange", "pear", "kiwi");
        Map<String, Integer> altMap = fruits.stream()
            .collect(Collectors.toMap(
                fruit -> fruit,     // Equivalent to identity
                String::length
            ));
            
        System.out.println("Alternative map: " + altMap);
    }
}

This example demonstrates how Function.identity provides a cleaner alternative to writing fruit -> fruit in stream collectors. It makes the code more readable and expresses the intent more clearly.

Identity in Grouping Operations

Another powerful use of Function.identity is with grouping collectors. When you need to group elements by themselves, identity serves as an elegant solution.

Main.java
package com.zetcode;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {

        List<String> words = List.of("apple", "banana", "apple", 
                                   "orange", "banana", "apple");
        
        // Count word occurrences using identity
        Map<String, Long> wordCounts = words.stream()
            .collect(Collectors.groupingBy(
                Function.identity(),
                Collectors.counting()
            ));
            
        System.out.println("Word counts: " + wordCounts);
        
        // Group strings by their identity
        Map<String, List<String>> grouped = words.stream()
            .collect(Collectors.groupingBy(Function.identity()));
            
        System.out.println("Grouped words: " + grouped);
    }
}

Here we see Function.identity used to group elements by themselves. The first collector counts occurrences of each word, while the second groups identical strings together. Both demonstrate clean, expressive code using the identity function.

Identity with Function Composition

Function.identity can be useful in function composition scenarios. It serves as a neutral element that doesn't affect the composition chain.

Main.java
package com.zetcode;

import java.util.function.Function;

public class Main {

    public static void main(String[] args) {

        // Create some transformation functions
        Function<String, String> toUpper = String::toUpperCase;
        Function<String, String> addExclamation = s -> s + "!";
        
        // Create identity function
        Function<String, String> identity = Function.identity();
        
        // Compose with identity (no effect)
        Function<String, String> composed1 = identity.andThen(toUpper);
        Function<String, String> composed2 = addExclamation.compose(identity);
        
        System.out.println("Composed1: " + composed1.apply("hello"));
        System.out.println("Composed2: " + composed2.apply("world"));
        
        // More complex composition
        Function<String, String> pipeline = identity
            .andThen(toUpper)
            .andThen(addExclamation)
            .andThen(identity);
            
        System.out.println("Pipeline result: " + pipeline.apply("java"));
    }
}

This example shows how Function.identity can be used in function composition without affecting the result. It serves as a neutral element in the composition chain, similar to how 0 is neutral in addition or 1 in multiplication.

Identity in Method References Context

Function.identity is often compared to method references. While similar in some cases, they serve different purposes. This example clarifies when to use each approach.

Main.java
package com.zetcode;

import java.util.function.Function;
import java.util.stream.Stream;

public class Main {

    public static void main(String[] args) {

        // Using identity in map operation
        Stream.of("one", "two", "three")
            .map(Function.identity())
            .forEach(System.out::println);
        
        // Equivalent using method reference
        Stream.of("uno", "dos", "tres")
            .map(s -> s)
            .forEach(System.out::println);
            
        // When method reference differs
        class Wrapper {
            private final String value;
            
            Wrapper(String value) { this.value = value; }
            
            String getValue() { return value; }
        }
        
        // Correct: method reference to getter
        Stream.of(new Wrapper("a"), new Wrapper("b"))
            .map(Wrapper::getValue)
            .forEach(System.out::println);
            
        // Incorrect: identity would return Wrapper objects
        Stream.of(new Wrapper("x"), new Wrapper("y"))
            .map(Function.identity())
            .forEach(w -> System.out.println(w.getValue()));
    }
}

This example demonstrates the difference between Function.identity and method references. While they might seem similar, identity always returns its input unchanged, whereas method references can transform objects by calling methods on them.

Identity in Advanced Stream Processing

For more complex stream processing scenarios, Function.identity can help maintain clean code when combined with other stream operations. Here's an advanced example.

Main.java
package com.zetcode;

import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {

        String[] colors = {"red", "green", "blue", "red", "green", "yellow"};
        
        // Advanced processing: count, filter, and map
        Map<String, Long> frequentColors = Arrays.stream(colors)
            .collect(Collectors.groupingBy(
                Function.identity(),
                Collectors.counting()
            ))
            .entrySet().stream()
            .filter(e -> e.getValue() > 1)
            .collect(Collectors.toMap(
                Map.Entry::getKey,
                e -> e.getValue() * 10  // Transform count
            ));
            
        System.out.println("Processed colors: " + frequentColors);
        
        // Another example: processing with identity
        Map<Boolean, Map<String, Long>> partitioned = Arrays.stream(colors)
            .collect(Collectors.partitioningBy(
                s -> s.length() > 3,
                Collectors.groupingBy(
                    Function.identity(),
                    Collectors.counting()
                )
            ));
            
        System.out.println("Partitioned counts: " + partitioned);
    }
}

This advanced example shows Function.identity in complex stream processing. It's used in grouping operations within a larger stream pipeline. The identity function helps keep focus on the transformations that matter while handling the grouping keys transparently.

Source

Java Function.identity Documentation

The Function.identity method is a simple yet powerful tool in Java's functional programming toolkit. It promotes cleaner code when working with streams and function composition. Understanding its proper use can make your Java code more expressive and maintainable.

Author

My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.

List all Java tutorials.