Functional interfaces used in Streams
Here’s an explanation of the five commonly used functional interfaces in Java Streams, along with their source code for reference:
1. Consumer<T>
Purpose: Represents an operation that consumes an element of a specific type (T
) from the stream but doesn’t return a value. Often used for printing or performing side effects.
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.forEach(name -> System.out.println(name)); // Prints each name
2. Function<T, R>
Purpose: Represents a function that takes an element of type T
as input and returns a value of type R
. Used for transforming elements within the stream.
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<String> doubledStrings = numbers.stream()
.map(number -> String.valueOf(number * 2)) // Function to double and convert to String
.collect(Collectors.toList());
System.out.println(doubledStrings); // Output: [2, 4, 6, 8, 10]
3. Predicate<T>
Purpose: Represents a test that takes an element of type T
and returns a boolean value. Used for filtering elements in the stream based on a condition.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
List<String> cities = Arrays.asList("London", "Tokyo", "New York", "Paris");
List<String> capitalCities = cities.stream()
.filter(city -> city.equals("London") || city.equals("Tokyo")) // Predicate checks for capitals
.collect(Collectors.toList());
System.out.println(capitalCities); // Output: [London, Tokyo]
4. Supplier<T>
Purpose: Represents a supplier that doesn’t take any arguments but returns a value of type T
. Used for generating new elements to be added to the stream.
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Stream<String> randomNames = Stream.generate(() -> "Name-" + Math.random()); // Supplier for random names
randomNames.limit(3).forEach(System.out::println); // Print 3 random names
5. BinaryOperator<T>
Purpose: Represents a function that takes two arguments of the same type T
and returns a value of type T
. Used for reduction operations like reduce
in streams, where elements are combined pairwise.
@FunctionalInterface
public interface BinaryOperator<T> {
T apply(T t1, T2 t2);
}
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, Integer::sum); // BinaryOperator for addition (using method reference)
System.out.println(sum); // Output: 15
When using the reduce()
operation in Java, there are two overloaded versions of the method:
reduce(BinaryOperator<T> accumulator)
: This version ofreduce()
only takes a binary operator and performs a reduction on the elements of the stream. The result is anOptional<T>
.reduce(T identity, BinaryOperator<T> accumulator)
: This version ofreduce()
takes both an initial value (identity
) and a binary operator. It performs a reduction on the elements of the stream, starting with theidentity
value and then applying the binary operator to each element successively. The result is the accumulated value.