ArrayList
contains() method
Object Evaluation
Java Programming
Data Structures

How does a ArrayList's contains() method evaluate objects?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

ArrayList.contains() looks simple, but it depends on Java equality rules in a way that surprises many developers. If you do not understand how equals() works, the method can return false even when two objects appear identical in the debugger. The key is that contains() checks logical equality one element at a time.

What contains() Actually Does

An ArrayList stores elements in order, backed by an array internally. When you call contains(value), Java scans the list from the beginning until it finds a match or reaches the end.

java
1import java.util.ArrayList;
2import java.util.List;
3
4public class Demo {
5    public static void main(String[] args) {
6        List<String> names = new ArrayList<>();
7        names.add("Ada");
8        names.add("Grace");
9
10        System.out.println(names.contains("Grace")); // true
11        System.out.println(names.contains("Linus")); // false
12    }
13}

Conceptually, the check behaves like this:

java
1for (Object element : list) {
2    if (value == null ? element == null : value.equals(element)) {
3        return true;
4    }
5}
6return false;

The exact library implementation is more polished, but the important detail is the same: contains() relies on equals(), not on object identity alone.

Equality for Custom Objects

For built-in types such as String, equals() is already implemented in a useful way. Two strings with the same characters are considered equal.

For your own classes, you must define equality yourself if you want contains() to treat separate instances as the same logical value.

java
1import java.util.ArrayList;
2import java.util.List;
3import java.util.Objects;
4
5class Person {
6    private final String name;
7    private final int age;
8
9    Person(String name, int age) {
10        this.name = name;
11        this.age = age;
12    }
13
14    @Override
15    public boolean equals(Object obj) {
16        if (this == obj) {
17            return true;
18        }
19        if (!(obj instanceof Person other)) {
20            return false;
21        }
22        return age == other.age && Objects.equals(name, other.name);
23    }
24
25    @Override
26    public int hashCode() {
27        return Objects.hash(name, age);
28    }
29}
30
31public class Demo {
32    public static void main(String[] args) {
33        List<Person> people = new ArrayList<>();
34        people.add(new Person("Maya", 28));
35
36        System.out.println(people.contains(new Person("Maya", 28))); // true
37    }
38}

Without that equals() override, the result would be false because the list would compare object references inherited from Object.

hashCode() Matters Indirectly

ArrayList.contains() does not use hashCode() directly. It performs a linear scan and calls equals() on elements until it finds a match.

Even so, you should still override hashCode() when you override equals(). Java collections such as HashSet and HashMap depend on both methods being consistent. If you define equality in one place and ignore hashCode(), your objects behave inconsistently across collections.

That means this is correct practice even when your immediate problem is only about ArrayList.contains().

Null Handling and Performance

contains() can search for null, and ArrayList can store null values.

java
1import java.util.ArrayList;
2import java.util.List;
3
4public class Demo {
5    public static void main(String[] args) {
6        List<String> values = new ArrayList<>();
7        values.add(null);
8        values.add("ready");
9
10        System.out.println(values.contains(null)); // true
11    }
12}

Performance is the other major consideration. contains() on an ArrayList is an O(n) operation because it may need to inspect every element. That is fine for small lists or occasional checks, but it becomes expensive when used repeatedly in large collections.

java
1for (String id : incomingIds) {
2    if (existingIds.contains(id)) {
3        // expensive if existingIds is large
4    }
5}

If fast membership tests matter more than element order, a HashSet is usually a better fit.

When contains() Seems Wrong

Most bugs around contains() come from mismatched equality definitions. Suppose you consider two users equal by email address only, but your equals() method compares both email and display name. A lookup that looks correct in business terms will still fail.

Another subtle issue is mutability. If equality depends on fields that can change after insertion, the meaning of membership can become confusing. With ArrayList, the scan still works, but the object may no longer represent the same logical record you thought you added.

A good rule is to base equality on stable identity fields and keep that rule obvious in the class design.

Common Pitfalls

The biggest mistake is forgetting to override equals() for custom classes. In that case, contains() falls back to reference comparison and separate but identical-looking objects will not match.

Another common problem is overriding equals() but not hashCode(). That may not break ArrayList.contains() directly, but it creates inconsistent behavior the moment the same type is used in hash-based collections.

Developers also run into performance issues by calling contains() inside nested loops. Since each call can scan the full list, the total cost grows quickly.

Finally, do not assume contains() uses sorting or binary search. Even if the list appears ordered, ArrayList.contains() still performs a linear check.

Summary

  • 'ArrayList.contains() checks elements one by one and uses equals() for comparison.'
  • Built-in types like String already define useful equality behavior.
  • Custom classes usually need equals() and hashCode() overrides.
  • 'contains() supports null values.'
  • Membership tests on large lists are O(n), so use HashSet when lookup speed matters.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.