contains()
Tests whether a list holds an equal element, and shows when equals, boxing or list size make the answer wrong or slow.
boolean contains(Object o)- Added in Java Java 1.2
Parameters
| Parameter | Type | Description |
|---|---|---|
$o | Object | The 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, socontainsreturns false. Use a record or implementequalsandhashCode. - Boxing to the wrong type. The parameter is
Object, soList<Long>.contains(2)compiles. The2boxes to anInteger, which is never equal to aLong. Write2L. - Case and whitespace.
"Admin"and"admin"differ. Normalise input before the check, or scan withanyMatch(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
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.
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
// trueCase-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.
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
// trueRequired scopes and fast lookups
containsAll expresses the all-required check directly. For repeated lookups, a Set built once answers each check in constant time.
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