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.
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.
Conceptually, the check behaves like this:
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.
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.
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.
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 usesequals()for comparison.' - Built-in types like
Stringalready define useful equality behavior. - Custom classes usually need
equals()andhashCode()overrides. - '
contains()supportsnullvalues.' - Membership tests on large lists are
O(n), so useHashSetwhen lookup speed matters.
Related reading
- How does a sentinel node offer benefits over NULL?
- How does Array.sort behave if comparison function is not transitive?
- How does AWS FIFO SQS deduplication ID work?
- How does ConcurrentHashMap handle rehashing?
- How does a Spring Boot console based application work?
- How does asychronous programming work in Netty? Does it make things more chatty?
- How does Dijkstra's Algorithm and A-Star compare?
- How does finding a cycle start node in a cycle linked list work?

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 courseTrack 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.