filter()

Keep only the elements that match a predicate, without touching the source collection, and know when filtering belongs in the database instead.

Signature
Stream<T> filter(Predicate<? super T> predicate)
  • Added in Java Java 8

Parameters

ParameterTypeDescription
$predicatePredicate<? super T>A non-interfering, stateless test applied to each element. Elements for which it returns true are kept.

Return value

Stream<T>A new stream containing only the elements for which the predicate returns true, in the same encounter order.

Errors

Nothing directly. Exceptions thrown by the predicate surface when the terminal operation runs. Passing a null predicate throws NullPointerException.

filter is an intermediate operation: it returns a new stream that lets through only the elements whose predicate returns true. Nothing happens until a terminal operation such as toList() or count() runs, and the source list is never changed.

Writing the predicate

The argument is a Predicate<? super T>, usually a lambda (o -> o.status() == Status.PAID) or a method reference (User::isActive). Two styles for several conditions both work:

orders.stream().filter(o -> o.paid()).filter(o -> o.total() > 100)
orders.stream().filter(o -> o.paid() && o.total() > 100)

Chained filters read well when each condition has a name. For reusable rules, build Predicate objects and combine them with and, or and negate. Predicate.not(String::isBlank) (Java 11) negates a method reference, which you cannot do with !.

Keep the predicate pure

The predicate should look at the element and return an answer. Adding to a list, incrementing a counter or changing the element inside filter makes the code depend on how many times and in what order the predicate runs. Laziness means it may run for fewer elements than you expect: with findFirst(), the stream stops at the first match.

filter versus removeIf

filter produces a new result and leaves the source alone. Collection.removeIf mutates the collection in place and returns whether anything was removed. Pick removeIf when you own a mutable list and want it trimmed; pick filter when the source is shared, immutable, or a query result you should not change.

Filter in the database, not in memory

The most expensive mistake with filter in back-end code is loading every row and filtering in Java: repository.findAll().stream().filter(u -> u.tenantId() == id). That pulls the whole table over the network and into the heap. Put the condition in the WHERE clause and use stream filtering for rules that cannot be expressed in SQL, or for small in-memory lists such as configuration and already-paged results.

Nulls

A predicate that calls a method on a null element throws NullPointerException. Put .filter(Objects::nonNull) first when the source can hold nulls, for example after mapping through a lookup table.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Return only the orders a customer is allowed to see after the database has already scoped them to the tenant.
  • Drop blank or malformed lines from an uploaded CSV before mapping them to import commands.
  • Pick out the feature flags that are enabled for the current environment from a small configuration list.

Frequently asked questions

How do I filter a stream by multiple conditions in Java?

Either combine them in one lambda with && and ||, chain several filter calls (each must pass), or build Predicate objects and join them with and(), or() and negate(). They produce the same result; chained or named predicates are easier to read and reuse.

Does Stream.filter modify the original list?

No. filter returns a new stream and the terminal operation builds a new result; the source collection is unchanged. If you want to remove elements from a mutable list in place, use Collection.removeIf.

How do I remove nulls from a stream?

Add .filter(Objects::nonNull) before any step that calls methods on the elements. For a single value that might be null, Stream.ofNullable(value) gives an empty stream instead of a stream containing null.

More examples

Combining named predicates

Named predicates turn validation rules into values you can combine and reuse across endpoints. Predicate.not wraps a method reference, which a leading ! cannot do.

Example · java
void main() {
    List<String> input = List.of("[email protected]", "  ", "[email protected]", "", "[email protected]");

    Predicate<String> notBlank = Predicate.not(String::isBlank);
    Predicate<String> exampleDomain = s -> s.endsWith("@example.com");

    IO.println(input.stream().filter(notBlank).toList());
    IO.println(input.stream().filter(notBlank.and(exampleDomain)).toList());
    IO.println(input.stream().filter(notBlank.and(exampleDomain.negate())).toList());
}
// Output:
// [[email protected], [email protected], [email protected]]
// [[email protected], [email protected]]
// [[email protected]]

filter is lazy and stops early with findFirst

The predicate never sees 5 or 20, because findFirst has its answer at 12. Any side effect placed in a predicate runs only for the elements that were actually pulled.

Example · java
void main() {
    Optional<Integer> first = Stream.of(3, 8, 12, 5, 20)
        .filter(n -> {
            IO.println("checking " + n);
            return n > 10;
        })
        .findFirst();
    IO.println(first.orElse(-1));
}
// Output:
// checking 3
// checking 8
// checking 12
// 12

filter leaves the source alone, removeIf does not

Use filter when you want a new result and removeIf when you deliberately want to trim a mutable collection you own, such as an in-memory session store.

Example · java
void main() {
    List<String> sessions = new ArrayList<>(List.of("active:1", "expired:2", "active:3"));

    List<String> active = sessions.stream()
        .filter(s -> s.startsWith("active"))
        .toList();
    IO.println(active);
    IO.println(sessions);

    boolean removed = sessions.removeIf(s -> s.startsWith("expired"));
    IO.println(removed + " " + sessions);
}
// Output:
// [active:1, active:3]
// [active:1, expired:2, active:3]
// true [active:1, active:3]

Discussion

  • Be the first to comment on this lesson.