Back to Functional Interfaces
Q4Question 4 of 13

What are BiConsumer, BiPredicate, BiFunction?

Key Concepts
  • BiConsumer takes two inputs and returns void: used by Map.forEach(key, value -> action)
  • BiFunction takes two inputs and returns a result: used by Map.merge, Map.compute, and Map.replaceAll
  • BiPredicate takes two inputs and returns boolean: used for conditions that need two arguments
  • The Bi prefix adds one more parameter to the single-argument counterpart
  • All compose with andThen (BiConsumer, BiFunction) or and/or/negate (BiPredicate)
  • For three or more arguments there are no built-in TriFunction/TriConsumer: use custom functional interfaces
  • Map operations are the primary use case for Bi variants in production code

Definition

BiConsumer, BiPredicate, and BiFunction are two-argument versions of the core functional interfaces, used primarily with Map operations like forEach, merge, and compute.
DIAGRAM
- **BiConsumer<T,U>** – `(T, U) -> void`; used by `Map.forEach(key, value -> action)`.
- **BiFunction<T,U,R>** – `(T, U) -> R`; used by `Map.merge`, `Map.compute`, `Map.replaceAll`.
- **BiPredicate<T,U>** – `(T, U) -> boolean`; used for two-input conditions, composable with `and()`, `or()`, `negate()`.
- **Relationship** – "Bi" adds one more parameter: Consumer(T->void) becomes BiConsumer(T,U->void).
- **No TriFunction** – for three or more arguments, define a custom `@FunctionalInterface`.

Consumer takes one thing and acts on it (process one cheque). BiConsumer takes two things (process a key-value pair: account number and balance). Map.forEach gives you both the key and the value, so you need BiConsumer to accept both.

Understanding the Concepts

What are BiConsumer, BiPredicate, BiFunction?

BiConsumer: two inputs, no return

JAVA
import java.math.BigDecimal;
import java.util.Map;
import java.util.function.BiConsumer;

Map<String, BigDecimal> balances = Map.of(
    "SAV2001", new BigDecimal("125000"),
    "CUR3001", new BigDecimal("850000"));

// Map.forEach takes BiConsumer<K, V>
balances.forEach((accountId, balance) -> {
    System.out.println("Account " + accountId + ": Rs." + balance);
});

// Composition: andThen chains two BiConsumers
BiConsumer<String, BigDecimal> logIt = (id, bal) -> System.out.println(id + ": " + bal);
BiConsumer<String, BigDecimal> auditIt = (id, bal) -> auditService.record(id, bal);
balances.forEach(logIt.andThen(auditIt));

BiFunction: two inputs, one return

JAVA
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

// Map.merge: BiFunction resolves key collision
Map<String, BigDecimal> totals = new HashMap<>();
trades.forEach(t -> {
    totals.merge(t.getSymbol(), t.getAmount(), BigDecimal::add);
});
// merge(key, newValue, BiFunction<existing, newValue, result>)

// Map.compute: BiFunction computes from key + existing value
balances.compute("SAV2001", (key, existing) -> {
    return existing == null ? BigDecimal.ZERO : existing.add(interest);
});

// Map.replaceAll: BiFunction transforms all values
balances.replaceAll((accountId, balance) ->
    balance.multiply(new BigDecimal("1.05")));  // 5% interest

BiPredicate: two inputs, boolean return

JAVA
import java.math.BigDecimal;
import java.util.function.BiPredicate;

BiPredicate<Trade, BigDecimal> exceedsLimit = (trade, limit) ->
    trade.getAmount().compareTo(limit) > 0;

// Composition:
BiPredicate<Trade, BigDecimal> exceedsAndSettled = exceedsLimit
    .and((trade, limit) -> trade.isSettled());

boolean result = exceedsLimit.test(trade, new BigDecimal("100000"));

What is the relationship to single-argument counterparts?

| Single | Bi | Difference |
|---|---|---|
| Consumer | BiConsumer | +1 input |
| Function | BiFunction | +1 input |
| Predicate | BiPredicate | +1 input |

What should a senior engineer know?

  • Map.merge with BigDecimal::add is the idiomatic way to aggregate by key in one line.
  • Map.compute returns null to remove the entry, or a new value to update it.
  • For three arguments, define a custom interface: @FunctionalInterface interface TriFunction { R apply(A a, B b, C c); }.

What are common interview follow-up questions?

  • What Map methods use BiFunction? (merge, compute, computeIfPresent, replaceAll.)
  • Can BiConsumer return a value? (No. Use BiFunction for that.)
  • Is there a TriConsumer? (No. Define your own or use currying.)

What are common mistakes?

JAVA
// WRONG: using Consumer when two inputs are needed
// Map.forEach needs BiConsumer, not Consumer
// balances.forEach(id -> System.out.println(id)); // COMPILE ERROR: expects (K, V)

// CORRECT: BiConsumer accepts two inputs
balances.forEach((id, balance) -> System.out.println(id + ": Rs." + balance));

Visual Overview

DIAGRAM
  Bi-variant Relationships:

  Consumer<T>         ──+1 param──▶  BiConsumer<T, U>
  T -> void                          (T, U) -> void
  list.forEach            Map.forEach

  Function<T, R>      ──+1 param──▶  BiFunction<T, U, R>
  T -> R                             (T, U) -> R
  stream.map              Map.merge, Map.compute

  Predicate<T>        ──+1 param──▶  BiPredicate<T, U>
  T -> boolean                       (T, U) -> boolean
  stream.filter           two-input conditions

Key Takeaway

BiConsumer, BiPredicate, and BiFunction are two-argument versions of the core functional interfaces. BiConsumer takes two inputs and returns void, used by Map.forEach. BiFunction takes two inputs and returns a result, used by Map.merge, Map.compute, and Map.replaceAll. BiPredicate takes two inputs and returns boolean.

The relationship is simple: Bi adds one more parameter. Consumer becomes BiConsumer, Function becomes BiFunction, Predicate becomes BiPredicate. For three or more arguments, there are no built-in variants; define a custom @FunctionalInterface.

In banking, Map.merge("RELIANCE", amount, BigDecimal::add) uses BiFunction to sum duplicate trades. Map.forEach((accountId, balance) -> log) uses BiConsumer. Map.replaceAll((id, bal) -> bal.multiply(rate)) applies interest to all balances.

Remember one sentence: BiConsumer, BiPredicate, and BiFunction are the two-argument versions of Consumer, Predicate, and Function, used primarily with Map operations.

---

Share
Use ← → arrow keys to navigate