Check if Element Exists in ArrayList: contains() and indexOf()

Learn how to check if element exists in ArrayList in Java with contains(), indexOf(), lastIndexOf() and anyMatch(), incl. custom objects and fast lookups.

A list of tags with indexes 0 to 3. indexOf(java) scans from the left and stops at index 0, lastIndexOf(java) scans from the right and stops at index 3, and contains(kotlin) scans every element and returns false.

The method contains(element) checks if an element exists in an ArrayList and returns true when the list holds at least one element equal to the argument, or false when it holds none. When we also need the position, indexOf() returns the index of the first match and lastIndexOf() the index of the last match, and both return -1 for a missing element.

We check for elements before we add a tag to a blog post, when we test whether a user has a role, or when we look up a value to replace or remove it. All three methods compare elements with equals(), so they work for strings, numbers and records without extra code, but not for every class we write.

The following example checks the tags of a blog post in several ways.

List<String> tags = new ArrayList<>(List.of("java", "spring", "docker", "java"));
boolean hasSpring = tags.contains("spring");                              // true
boolean hasKotlin = tags.contains("kotlin");                              // false
int first = tags.indexOf("java");                                         // 0
int last = tags.lastIndexOf("java");                                      // 3
int missing = tags.indexOf("kotlin");                                     // -1
boolean both = tags.containsAll(List.of("java", "docker"));               // true
boolean ignoreCase = tags.stream().anyMatch("Spring"::equalsIgnoreCase);  // true

Notice that contains(“Spring”) with a capital S would return false, because equals() is case-sensitive, so the last line uses a stream. We cover contains(), indexOf() and lastIndexOf() one by one, and after them custom objects, conditions, several elements at once and faster lookups for large lists.

1. Checking an Element with contains()

The method contains(Object o) walks the list from index 0 and stops at the first element that is equal to o. In the OpenJDK source, it is a one-line method that returns indexOf(o) >= 0, so a match at index 0 counts too.

A list of tags with indexes 0 to 3. indexOf(java) scans from the left and stops at index 0, lastIndexOf(java) scans from the right and stops at index 3, and contains(kotlin) scans every element and returns false.
The method indexOf() scans from the start, lastIndexOf() from the end, and contains() is indexOf() >= 0
List<String> tags = new ArrayList<>(List.of("java", "spring", "docker"));
boolean found = tags.contains("docker");      // true
boolean upper = tags.contains("Docker");      // false, equals() is case-sensitive

List<String> drafts = new ArrayList<>(Arrays.asList("java", null));
boolean hasNull = drafts.contains(null);      // true

An ArrayList accepts null elements, and contains(null) finds them. The method needs time in proportion to the list size, because a missing element means comparing it with every element in the list, which is fine for tens or hundreds of elements.

2. Finding the Position with indexOf()

The method indexOf(Object o) returns the index of the first element equal to o, or -1 when the list has no such element. We use it instead of contains() when the next step needs the position, such as replacing or removing the element.

List<String> tags = new ArrayList<>(List.of("java", "spring", "java", "docker"));
int index = tags.indexOf("java");             // 0, the first of two matches
int none = tags.indexOf("kotlin");            // -1

The result -1 is a valid int but not a valid index, so we check it before passing it on. Without the check, set(-1, value) or get(-1) throws an IndexOutOfBoundsException.

List<String> tags = new ArrayList<>(List.of("java", "spring", "docker"));
int pos = tags.indexOf("spring");                               // 1
String old = pos >= 0 ? tags.set(pos, "spring-boot") : null;    // "spring", tags = [java, spring-boot, docker]
String unsafe = tags.get(tags.indexOf("kotlin"));               // IndexOutOfBoundsException: Index -1 out of bounds for length 3

The replace element in ArrayList and remove element from ArrayList articles build on this pattern.

3. Finding the Last Position with lastIndexOf()

The method lastIndexOf(Object o) searches from the end of the list and returns the index of the last element equal to o, or -1 when there is none. For a value that occurs once, indexOf() and lastIndexOf() return the same index.

List<String> tags = new ArrayList<>(List.of("java", "spring", "java", "docker", "java"));
int lastJava = tags.lastIndexOf("java");      // 4
int onlyOne = tags.lastIndexOf("docker");     // 3, same as indexOf("docker")
int absent = tags.lastIndexOf("kotlin");      // -1

Comparing both results tells us whether a value is a duplicate. If indexOf(x) and lastIndexOf(x) differ, the list holds x at least twice.

List<String> tags = new ArrayList<>(List.of("java", "spring", "java"));
boolean javaTwice = tags.indexOf("java") != tags.lastIndexOf("java");       // true
boolean springTwice = tags.indexOf("spring") != tags.lastIndexOf("spring"); // false

4. Checking Custom Objects in an ArrayList

All three methods call equals(). A class that does not override equals() inherits the version from Object, which compares references, so contains() finds only the very same object and returns false for an equal copy.

static class Author {
    String name;
    Author(String name) { this.name = name; }
}
List<Author> authors = new ArrayList<>(List.of(new Author("Lokesh")));
boolean copyFound = authors.contains(new Author("Lokesh"));   // false, no equals() override
boolean sameFound = authors.contains(authors.get(0));         // true, same reference

A record gets equals() and hashCode() from the compiler, based on all its components, so an equal copy matches. For a regular class, we override both methods, as the hashCode() and equals() article explains.

record Tag(String name, int posts) {}
List<Tag> tags = new ArrayList<>(List.of(new Tag("java", 120), new Tag("spring", 80)));
boolean match = tags.contains(new Tag("java", 120));          // true
boolean otherCount = tags.contains(new Tag("java", 99));      // false, posts differs

When we search by one field instead of the whole object, contains() is the wrong tool, and a stream with anyMatch() is the right one. The next section shows that check.

5. Checking by a Condition or Ignoring Case

The method anyMatch() takes a Predicate and returns true as soon as one element matches it, so it stops at the first match like contains() does. We use it for case-insensitive checks and for searches by a field.

List<Tag> tags = List.of(new Tag("java", 120), new Tag("spring", 80));
boolean popular = tags.stream().anyMatch(t -> t.posts() > 100);           // true
boolean hasJava = tags.stream().anyMatch(t -> t.name().equals("java"));   // true

List<String> names = List.of("java", "spring");
boolean caseless = names.stream().anyMatch("JAVA"::equalsIgnoreCase);     // true

For the position of the first element that matches a condition, we stream over the indexes with IntStream, because a stream of elements loses the index.

List<Tag> tags = List.of(new Tag("java", 120), new Tag("spring", 80), new Tag("docker", 40));
int firstSmall = IntStream.range(0, tags.size()).filter(i -> tags.get(i).posts() < 100).findFirst().orElse(-1);   // 1

6. Checking Several Elements at Once

The method containsAll(Collection c) returns true when the list holds every element of c. To check whether the list holds at least one element of c, we use Collections.disjoint(), which returns true when the two collections have no element in common, so we negate it.

List<String> tags = new ArrayList<>(List.of("java", "spring", "docker"));
boolean all = tags.containsAll(List.of("java", "spring"));                // true
boolean notAll = tags.containsAll(List.of("java", "kotlin"));             // false
boolean any = !Collections.disjoint(tags, List.of("kotlin", "docker"));   // true

To count how many times a value occurs, Collections.frequency() returns the number of equal elements, and 0 means the value is absent. The contains, containsAny and containsAll article compares more variants with streams.

List<String> tags = List.of("java", "spring", "java");
int javaCount = Collections.frequency(tags, "java");      // 2
int kotlinCount = Collections.frequency(tags, "kotlin");  // 0

7. Faster Lookups in Large Lists

Every contains() call on a list scans it, so checking 10,000 values against a list of 10,000 elements means up to 100 million comparisons. When the same list is checked many times, we copy it once into a HashSet, whose contains() takes constant time on average.

List<String> blocked = List.of("spam", "ads", "casino");
Set<String> blockedSet = new HashSet<>(blocked);
boolean isBlocked = blockedSet.contains("casino");        // true

When the list is sorted, Collections.binarySearch() finds an element in logarithmic time without a second collection. It returns the index for a match, or a negative number for a missing element, and on an unsorted list the result is undefined.

List<String> sorted = new ArrayList<>(List.of("docker", "java", "kotlin", "spring"));
int at = Collections.binarySearch(sorted, "kotlin");      // 2
int gone = Collections.binarySearch(sorted, "rust");      // -4, negative means missing

8. Rejecting Duplicate Tags in a Blog Editor

Say a blog editor lets an author type tags for a post. The editor must reject a tag that the post already has, even when the author types it with different case, and must add new tags in lowercase. A plain contains() would let “Java” in next to “java”.

static boolean addTag(List<String> tags, String input) {
    String tag = input.strip().toLowerCase();
    if (tag.isEmpty() || tags.contains(tag)) {
        return false;
    }
    return tags.add(tag);
}
List<String> tags = new ArrayList<>(List.of("java", "spring"));
boolean added = addTag(tags, " Docker ");     // true, tags = [java, spring, docker]
boolean dupe = addTag(tags, "JAVA");          // false, tags = [java, spring, docker]
boolean blank = addTag(tags, "   ");          // false

Storing every tag in lowercase keeps the list normalized, so the fast contains() check is enough and no stream with equalsIgnoreCase() is needed. A post has a handful of tags, so a list is fine here, whereas a site-wide tag index with thousands of entries would use a Set.

9. ArrayList contains() FAQs

Readers who check list elements often ask about null values, custom classes and speed.

9.1. Does contains() Work with null?

Yes for an ArrayList, which returns true when it holds a null element. A list from List.of() or List.copyOf() behaves differently. It never holds null, and its contains(null) throws a NullPointerException instead of returning false.

List<String> fixed = List.of("java", "spring");
boolean nullCheck = fixed.contains(null);                 // NullPointerException

9.2. Why Does contains() Return false for an Object That Is in the List?

The class of the element does not override equals(), or its equals() compares fields that differ, as we saw in section 4. We override equals() and hashCode(), use a record, or search by a field with anyMatch().

9.3. What Is the Difference Between contains() and indexOf()?

Both run the same search. The method contains() returns a boolean, and indexOf() returns the position or -1. We call contains() when we only ask “is it there?” because it reads better, and indexOf() when we need the index next.

9.4. What Is the Time Complexity of ArrayList contains()?

Linear. In the worst case, the method compares the argument with every element. A HashSet answers the same question in constant time on average, and Collections.binarySearch() in logarithmic time on a sorted list.

9.5. How Do We Check if an ArrayList Is Empty?

We call isEmpty(), which returns true for a list with no elements. The check if ArrayList is empty article covers null-safe variants.

10. Conclusion

To check if an element exists in an ArrayList, we call contains(), and to get its position, indexOf() for the first match or lastIndexOf() for the last. All three compare with equals(), so our own classes need an equals() override, while records work as they are.

For case-insensitive checks and searches by a field, stream().anyMatch() does the job. When a large list is checked often, a HashSet or Collections.binarySearch() on a sorted list replaces the linear scan.

11. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. ArrayList<String> list = new ArrayList<>(2);
    list.add(“A”);
    list.add(“B”);
    list.add(“C”);
    list.add(“D”);
    System.out.println( list.indexOf(“A”) > 0 );    //true
    System.out.println( list.indexOf(“Z”) > 0);    //false

    This code logic is wrong as indexOf(“A”) return 0 as List maintains insertion order and indexOf(“A”) is at first position so it will return 0 and 0>0 will always return false so the correction should be 0>=0.
    Please let me know I am correct or not.

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.