contains()

Tests whether a list holds an equal element, and shows when equals, boxing or list size make the answer wrong or slow.

Signature
boolean contains(Object o)
  • Added in Java Java 1.2

Parameters

ParameterTypeDescription
$oObjectThe value to look for. Its type is not checked at compile time.

Return value

booleantrue if at least one element e satisfies Objects.equals(o, e).

Errors

ClassCastException if the type is incompatible and the list checks it (optional). NullPointerException if o is null and the list does not allow nulls, as with List.of.

contains(o) scans the list and returns true as soon as it finds an element equal to o. Equality means equals, not ==, so two separate String objects with the same text match, and two records with the same components match.

When it returns false unexpectedly

  • No equals method. A plain class inherits identity equality from Object. A new instance with the same fields is a different object, so contains returns false. Use a record or implement equals and hashCode.
  • Boxing to the wrong type. The parameter is Object, so List<Long>.contains(2) compiles. The 2 boxes to an Integer, which is never equal to a Long. Write 2L.
  • Case and whitespace. "Admin" and "admin" differ. Normalise input before the check, or scan with anyMatch(s -> s.equalsIgnoreCase(x)).

Because the parameter is Object, passing a value of an unrelated type is not a compile error. Most IDEs warn about it; take the warning seriously.

Null

On an ArrayList, contains(null) is fine and returns whether a null is present. On lists from List.of and List.copyOf it throws NullPointerException. Values from optional request fields are the usual source of that null.

Speed

contains on a list is linear: it may call equals on every element. For a list of five allowed roles that does not matter. For checking 10,000 incoming ids against 10,000 known ids it means up to 100 million comparisons. Copy the list into a HashSet (or Set.copyOf) once and look up in that, which is constant time per check.

containsAll(collection) checks that every element of the argument is present, which fits checks such as "does this token have all the required scopes".

Example

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

When to use it

  • Checking that a requested role or OAuth scope is in the caller's granted list.
  • Rejecting a sort field that is not in the endpoint's list of allowed columns.
  • Skipping a webhook event whose type is not in the subscribed list.

Frequently asked questions

Why does list.contains return false for my object?

contains uses equals. If your class does not override equals (and hashCode), only the same instance matches. Use a record or implement equals based on the fields that define identity.

Is List.contains slow?

It is linear in the size of the list. That is fine for small lists, but for many lookups against a large list, copy it into a HashSet once and call contains on the set.

How do I check if a list contains a string ignoring case?

Use list.stream().anyMatch(s -> s.equalsIgnoreCase(value)), or store and compare lower-cased values using toLowerCase(Locale.ROOT).

More examples

Classes need equals, records have it

TagClass uses the identity equals inherited from Object, so a new instance never matches. The record compares its components and matches.

Example · java
class TagClass {
    final String name;
    TagClass(String name) { this.name = name; }
}

record TagRecord(String name) {}

void main() {
    List<TagClass> classes = List.of(new TagClass("java"));
    List<TagRecord> records = List.of(new TagRecord("java"));

    IO.println(classes.contains(new TagClass("java")));
    IO.println(records.contains(new TagRecord("java")));
}
// Output:
// false
// true

Case-insensitive membership

contains is exact. Either scan with equalsIgnoreCase or normalise the input once, using Locale.ROOT so the result does not depend on the server's locale.

Example · java
void main() {
    List<String> blocked = List.of("spam.example", "junk.example");
    String domain = "SPAM.example";

    IO.println(blocked.contains(domain));
    IO.println(blocked.stream().anyMatch(d -> d.equalsIgnoreCase(domain)));
    IO.println(blocked.contains(domain.toLowerCase(Locale.ROOT)));
}
// Output:
// false
// true
// true

Required scopes and fast lookups

containsAll expresses the all-required check directly. For repeated lookups, a Set built once answers each check in constant time.

Example · java
void main() {
    List<String> granted = List.of("orders:read", "orders:write", "users:read");
    List<String> required = List.of("orders:read", "orders:write");
    IO.println(granted.containsAll(required));

    Set<String> grantedSet = Set.copyOf(granted);
    List<String> requested = List.of("users:read", "users:delete");
    for (String scope : requested) {
        IO.println(scope + " " + grantedSet.contains(scope));
    }
}
// Output:
// true
// users:read true
// users:delete false

Discussion

  • Be the first to comment on this lesson.