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:

  1. reduce(BinaryOperator<T> accumulator): This version of reduce() only takes a binary operator and performs a reduction on the elements of the stream. The result is an Optional<T>.
  2. reduce(T identity, BinaryOperator<T> accumulator): This version of reduce() takes both an initial value (identity) and a binary operator. It performs a reduction on the elements of the stream, starting with the identity value and then applying the binary operator to each element successively. The result is the accumulated value.

You may also like...