1. Overview

In this short tutorial, we’ll learn how to avoid inserting duplicate values when using ArrayList in Java.

First, we’ll see how to do this using ready-to-use JDK classes. Then, we’ll discuss how to achieve the same objective using external libraries such as Apache Commons Collections and Guava.

2. Set Class

Typically, the Set class provides the easiest solution to tackle our challenge. At its core, it denotes a collection that doesn’t allow duplicate elements. So, let’s see it in action:

@Test
void givenArrayList_whenUsingSet_thenAvoidDuplicates() {
    Set<String> distinctCities = new HashSet<>(Arrays.asList("Tamassint", "Madrid", "Paris", "Tokyo"));

    String newCity = "Paris";
    distinctCities.add(newCity);
    ArrayList<String> arrayListCities = new ArrayList<>(distinctCities);

    assertThat(arrayListCities).hasSameSizeAs(distinctCities);
}

As we can see, we attempted to add a duplicate city “Paris” to our Set of cities. However, the ArrayList created from the Set doesn’t contain the newly added city. As a rule of thumb, using Set implementations instead of other collections is always the best approach to avoid duplicate values.

3. List#contains Method

The second option we’ll look at is the contains() method of the List interface. As the name indicates, it checks the presence of a given element in a list. It returns true if the element is present and false otherwise:

@Test
void givenArrayList_whenUsingContains_thenAvoidDuplicates() {
    List<String> distinctCities = Arrays.asList("Tamassint", "Madrid", "Paris", "Tokyo");
    ArrayList<String> arrayListCities = new ArrayList<>(distinctCities);

    String newCity = "Madrid";
    if (!arrayListCities.contains(newCity)) {
        arrayListCities.add(newCity);
    }

    assertThat(arrayListCities).hasSameSizeAs(distinctCities);
}

Here, we checked if arrayListCities already contains the new city “Madrid”. Since the city is already in the list, the condition evaluates to false, so arrayListCities.add() isn’t executed, and “Madrid” isn’t added again.

4. Stream#anyMatch Method

The Java 8 Stream API provides a convenient way to answer the central question. It comes with the anyMatch() method that we can use to check whether any elements of the stream match a specified predicate.

So, let’s see it in practice:

@Test
void givenArrayList_whenUsingAnyMatch_thenAvoidDuplicates() {
    List<String> distinctCities = Arrays.asList("Tamassint", "Madrid", "Paris", "Tokyo");
    ArrayList<String> arrayListCities = new ArrayList<>(distinctCities);

    String newCity = "Tamassint";
    boolean isCityPresent = arrayListCities.stream()
      .anyMatch(city -> city.equals(newCity));
    if (!isCityPresent) {
        arrayListCities.add(newCity);
    }

    assertThat(arrayListCities).hasSameSizeAs(distinctCities);
}

Similarly, we used anyMatch() to check if the ArrayList contains the new city “Tamassint”. Since it’s already present, anyMatch() returns true, and the method add() isn’t called which avoids adding a duplicate city.

5. Stream#filter Method

Alternatively, we can use the filter() method to achieve the same outcome. In short, this method filters a Stream of elements based on a provided predicate:

@Test
void givenArrayList_whenUsingFilterAndFindFirst_thenAvoidDuplicates() {
    List<String> distinctCities = Arrays.asList("Tamassint", "Madrid", "Paris", "Tokyo");
    ArrayList<String> arrayListCities = new ArrayList<>(distinctCities);

    String newCity = "Tamassint";
    Optional<String> optionalCity = arrayListCities.stream()
      .filter(city -> city.equals(newCity))
      .findFirst();
    if (optionalCity.isEmpty()) {
        arrayListCities.add(newCity);
    }

    assertThat(arrayListCities).hasSameSizeAs(distinctCities);
}

In a nutshell, we created a stream from the ArrayList. Furthermore, we used the filter() method to find any element that equals the newly added city “Tamassint”. Then, we invoked findFirst() which returns an Optional containing the first element of the filtered stream.

Moreover, we checked if the returned Optional object is empty. As a result “Tamassint” isn’t added because it’s already present in the ArrayList.

6. Using Guava

Another solution would be to use Guava. To work with this library, we’ll first need to add its Maven dependency to our pom.xml:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>33.2.0-jre</version>
</dependency>

Guava offers the Iterables utility class that contains static methods operating on collections. Among these methods, we find the contains() method. So, let’s exemplify how to avoid inserting duplicate elements using this utility method:

@Test
void givenArrayList_whenUsingIterablesContains_thenAvoidDuplicates() {
    List<String> distinctCities = Arrays.asList("Tamassint", "Madrid", "Paris", "Tokyo");
    ArrayList<String> arrayListCities = new ArrayList<>(distinctCities);

    String newCity = "Paris";
    boolean isCityPresent = Iterables.contains(arrayListCities, newCity);
    if (!isCityPresent) {
        arrayListCities.add(newCity);
    }

    assertThat(arrayListCities).hasSameSizeAs(distinctCities);
}

This method is straightforward and readable. However, it’s likely to be used only if we’re already using Guava, considering the same built-in Java method is available.

7. Using Apache Commons Collections

Our final solution is to use the containsAny() method from the CollectionUtils class. As always, let’s add the Apache Commons Collections dependency to the pom.xml file:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.5.0-M2</version>
</dependency>

Fundamentally, containsAny() verifies if given elements are present in a specified collection. It uses the intersection() method under the hood and returns true if the intersection isn’t empty:

@Test
void givenArrayList_whenUsingCollectionUtilsContainsAny_thenAvoidDuplicates() {
    List<String> distinctCities = Arrays.asList("Tamassint", "Madrid", "Paris", "Tokyo");
    ArrayList<String> arrayListCities = new ArrayList<>(distinctCities);

    String newCity = "Tokyo";
    boolean isCityPresent = CollectionUtils.containsAny(arrayListCities, newCity);
    if (!isCityPresent) {
        arrayListCities.add(newCity);
    }

    assertThat(arrayListCities).hasSameSizeAs(distinctCities);
}

As expected, the test was executed successfully.

8. Conclusion

In this article, we’ve seen that the Set interface is the most versatile solution to avoid adding duplicate elements. Additionally, we’ve explored different alternatives to achieve the same result such as the Stream API, Guava, and Apache Commons Collections.

As always, the full code for the examples is available over on GitHub.