Java
ArrayList
Duplicates
Data Structures
Programming

Java - Removing duplicates in an ArrayList

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

Removing duplicates from an ArrayList is easy once you decide which behavior you actually need. Some solutions keep insertion order, some do not, and some rely on correct equals and hashCode implementations for custom objects. The best approach depends on whether you care most about speed, order preservation, or mutating the original list in place.

Use LinkedHashSet to Remove Duplicates and Keep Order

For many cases, the cleanest solution is converting the list to a LinkedHashSet and back. LinkedHashSet removes duplicates while preserving insertion order.

java
1import java.util.ArrayList;
2import java.util.LinkedHashSet;
3import java.util.List;
4
5public class Demo {
6    public static void main(String[] args) {
7        List<String> values = new ArrayList<>(List.of("a", "b", "a", "c", "b"));
8        List<String> unique = new ArrayList<>(new LinkedHashSet<>(values));
9        System.out.println(unique);
10    }
11}

This is usually the best default when list order matters.

Use HashSet Only If Order Does Not Matter

If insertion order is irrelevant, HashSet is slightly simpler conceptually, but it does not preserve the original order.

java
1import java.util.ArrayList;
2import java.util.HashSet;
3import java.util.List;
4
5public class Demo {
6    public static void main(String[] args) {
7        List<Integer> numbers = new ArrayList<>(List.of(3, 1, 3, 2, 1));
8        List<Integer> unique = new ArrayList<>(new HashSet<>(numbers));
9        System.out.println(unique);
10    }
11}

Do not choose this if the resulting list is shown to users or used in order-sensitive logic.

Use Streams for Readable Pipeline Code

Java Streams offer a concise way to express de-duplication:

java
1import java.util.List;
2
3public class Demo {
4    public static void main(String[] args) {
5        List<String> values = List.of("red", "blue", "red", "green");
6        List<String> unique = values.stream().distinct().toList();
7        System.out.println(unique);
8    }
9}

distinct() preserves encounter order for ordered streams, which makes it a good modern option when you already use stream pipelines.

In-Place Removal with a Tracking Set

If you must mutate the existing ArrayList, use removeIf with a tracking set.

java
1import java.util.ArrayList;
2import java.util.HashSet;
3import java.util.List;
4import java.util.Set;
5
6public class Demo {
7    public static void main(String[] args) {
8        List<String> values = new ArrayList<>(List.of("x", "y", "x", "z", "y"));
9        Set<String> seen = new HashSet<>();
10        values.removeIf(item -> !seen.add(item));
11        System.out.println(values);
12    }
13}

This preserves the first occurrence and removes later duplicates from the same list object.

Custom Objects Need Correct Equality

For custom object lists, duplicate removal depends on equals and hashCode. If those are not implemented consistently, set-based approaches and distinct() will behave incorrectly.

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

Without correct equality semantics, Java cannot know what "duplicate" means.

Common Pitfalls

  • Using HashSet when insertion order must be preserved.
  • Forgetting that custom objects need correct equals and hashCode.
  • Removing duplicates in place when callers expected the original list to remain unchanged.
  • Using nested loops for large lists when set-based solutions are simpler and faster.
  • Assuming distinct() changes the original list rather than returning a new stream result.

Summary

  • 'LinkedHashSet is the simplest ordered de-duplication strategy for many ArrayList cases.'
  • 'HashSet works when order is irrelevant.'
  • Stream distinct() is a clean option in modern pipeline-style code.
  • 'removeIf plus a tracking set is useful when you need in-place mutation.'
  • For custom types, duplicate removal is only as good as your equals and hashCode methods.

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.