Java
ArrayList
Search
Programming
Data Structures

Check if a value exists in ArrayList

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

To check whether an ArrayList contains a value, the normal answer in Java is contains(). That method is simple and readable, but it is only as correct as the equals() implementation used for comparison. For small lists and occasional checks, contains() is exactly the right tool. For repeated lookups or custom objects, you need to understand what it is really doing.

Use contains() For Normal Cases

ArrayList.contains(value) returns true if any element in the list is equal to the given value.

java
1import java.util.ArrayList;
2import java.util.List;
3
4public class Main {
5    public static void main(String[] args) {
6        List<String> fruits = new ArrayList<>();
7        fruits.add("apple");
8        fruits.add("banana");
9        fruits.add("cherry");
10
11        System.out.println(fruits.contains("apple"));
12        System.out.println(fruits.contains("orange"));
13    }
14}

Under the hood, contains() scans the list from beginning to end and compares elements using equals(). That means the time complexity is linear, or O(n).

For many applications, that is completely fine. If the list has a few dozen elements, clarity matters more than micro-optimization.

What Happens With Custom Objects

The important detail is that contains() depends on equals(). If you store custom objects and do not define equality properly, a logically identical value may not be found.

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

Because equals() and hashCode() are defined, the lookup behaves as expected. Without that override, the comparison would use object identity and return false.

When A Manual Search Is Better

Sometimes you do not want exact object equality. You may want to search by one field or perform a case-insensitive match. In that case, a loop or a stream can be clearer than contains().

java
1import java.util.ArrayList;
2import java.util.List;
3
4public class Main {
5    public static void main(String[] args) {
6        List<String> fruits = new ArrayList<>(List.of("Apple", "Banana", "Cherry"));
7
8        boolean found = fruits.stream().anyMatch("apple"::equalsIgnoreCase);
9        System.out.println(found);
10    }
11}

That expresses the real comparison rule directly instead of pretending the list contains the exact same object or string form.

A traditional loop is equally valid and sometimes easier to debug.

java
1boolean found = false;
2for (String fruit : fruits) {
3    if (fruit.equalsIgnoreCase("apple")) {
4        found = true;
5        break;
6    }
7}

When ArrayList Is The Wrong Data Structure

If you need to check membership many times, an ArrayList may not be the right collection. A HashSet provides average O(1) membership checks.

java
1import java.util.HashSet;
2import java.util.Set;
3
4public class Main {
5    public static void main(String[] args) {
6        Set<String> fruitSet = new HashSet<>(Set.of("apple", "banana", "cherry"));
7        System.out.println(fruitSet.contains("banana"));
8    }
9}

This matters when the collection is large or membership checks happen frequently in performance-sensitive code.

Use ArrayList when order and indexed access matter. Use Set when fast existence checks matter more.

Common Pitfalls

The biggest mistake is forgetting that contains() uses equals(). For custom classes, default object identity is usually not what you want.

Another common issue is expecting contains() to be fast on very large lists. It scans linearly, so repeated lookups can become expensive.

Developers also sometimes use contains() when the real rule is more specific, such as case-insensitive matching or matching one property of an object. In those cases, use a loop or stream().anyMatch(...).

Finally, do not switch to a HashSet blindly. Sets improve membership lookup, but they do not preserve the same indexed semantics as ArrayList.

Summary

  • Use contains() for simple membership checks on an ArrayList.
  • 'contains() relies on equals(), so custom object equality must be defined correctly.'
  • Use stream().anyMatch(...) or a loop for custom comparison rules.
  • Membership checks on ArrayList are O(n).
  • For repeated lookups, consider HashSet instead.
  • Choose the collection based on both lookup needs and the semantics your code actually requires.

Course illustration
Course illustration

All Rights Reserved.