Contains Duplicate in Java DSA: Brute Force, Sorting, and the HashSet Trick
-
Last Updated: July 12, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Learn the Contains Duplicate in Java DSA problem the easy way. Compare brute force, sorting, and the fast HashSet approach with a clear beginner dry run.
Contains Duplicate in Java is one of those warm-up questions you meet early in any array series. It looks almost too simple. But it quietly teaches you how a HashSet works, and that lesson shows up again and again in harder problems.
Here is the task in plain words. You get an array of numbers. You must answer one yes-or-no question: Does any value appear more than once? If even a single number repeats, you return true. If every number is unique, you return false.
In this guide, we build up slowly. First, we try the obvious brute force. Then we speed it up with sorting. Finally, we reach the clean HashSet solution that most interviewers want to see. Along the way, we dry-run the code by hand, so you can watch it work step by step.
Let us lock down the rules before we write any code. A clear problem statement saves you from silly bugs.
[1, 2, 3, 1].So for [1, 2, 3, 1] the answer is true, because 1 shows up at index 0 and again at index 3. For [1, 2, 3, 4] the answer is false, since nothing repeats. Notice we return a boolean here, not an index. Beginners sometimes overthink this and start hunting for positions.
| 💡 Interview Insight Interviewers love to ask what you would do if the array were huge and could not fit in memory. Mentioning that a HashSet trades memory for speed, and that sorting avoids extra memory, shows you understand the trade-off before they even push on it. |
Before the code, let us name the two ideas that carry this whole problem. Both are small, and you will lean on them in many array questions later.
A HashSet stores unique values and answers one question fast: is this value already inside? On average, that check takes constant time. So you skip scanning the array again and again. Instead, you keep a running memory of what you have seen, and you just ask the set.
The second idea is simpler. When you sort an array, equal values end up sitting next to each other. So a duplicate is never far away; it is always the neighbor. That turns a wide search into a quick side-by-side check.
The first idea most people reach for is simple. Compare every number with every other number. If any two match, you found a duplicate.
for i from 0 to n-1:
for j from i+1 to n-1:
if nums[i] == nums[j]:
return true
return false // no duplicate foundThe outer loop picks one number. The inner loop checks it against every later number. We start j at i plus 1, so we never compare a number with itself. This also avoids checking the same pair twice.
public class ContainsDuplicateBrute {
public static boolean containsDuplicate(int[] nums) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] == nums[j]) {
return true;
}
}
}
return false;
}
public static void main(String[] args) {
int[] nums = { 1, 2, 3, 1 };
System.out.println(containsDuplicate(nums)); // true
}
}Let us walk through the code slowly. Each line has one clear job.
i points at the first number in a pair.j points at a number after i, so we test i against every later value.nums[i] with nums[j]. A match means we found a repeat.This code is easy to read and hard to get wrong. That is its strength. The weakness is speed, which we fix next.
Brute force checks almost every pair. For an array of size n, that is roughly n times n over two comparisons. So the time is O(n squared). The space stays O(1), because we only use a couple of loop variables. For small arrays, this is fine. For large ones, it drags.
Here is a neat middle-ground idea. If you sort the array, then any duplicate values sit right next to each other. So you only need to check each number against its neighbor.
sort(nums)
for i from 1 to n-1:
if nums[i] == nums[i-1]:
return true
return falseimport java.util.Arrays;
public class ContainsDuplicateSort {
public static boolean containsDuplicate(int[] nums) {
Arrays.sort(nums);
for (int i = 1; i < nums.length; i++) {
if (nums[i] == nums[i - 1]) {
return true;
}
}
return false;
}
public static void main(String[] args) {
int[] nums = { 1, 2, 3, 1 };
System.out.println(containsDuplicate(nums)); // true
}
}Arrays.sort(nums), which reorders the values from smallest to largest.Sorting takes O(n log n) time, which beats brute-force on large inputs. The catch is that Arrays.sort changes the original order of your array. If the caller still needs the original order, this approach can surprise them.
Now for the version interviewers really want. The idea is to remember every number as you walk the array once. Before you store a number, you first ask a simple question: Have I seen this value already?
If the value is already in your memory, you found a duplicate and return true. If not, you add it and move on. A HashSet is perfect here, because it checks membership in constant time on average.
seen = empty set
for each num in nums:
if num is in seen:
return true
add num to seen
return falseimport java.util.HashSet;
public class ContainsDuplicateHashSet {
public static boolean containsDuplicate(int[] nums) {
HashSet<Integer> seen = new HashSet<>();
for (int num : nums) {
if (seen.contains(num)) {
return true;
}
seen.add(num);
}
return false;
}
public static void main(String[] args) {
int[] nums = { 1, 2, 3, 1 };
System.out.println(containsDuplicate(nums)); // true
}
}This version does the most thinking, so let us break it down gently.
HashSet called seen. It holds every value we have looked at so far.seen.contains(num). If it returns true, this value already appeared, so we return true.seen.add(num) and continue to the next number.Because a HashSet lookup is constant on average, one pass through the array is enough. That single idea is what makes this approach shine.
| 💡 Interview Insight A common follow-up asks why we check before we add, instead of adding first. If you add first, the set already holds the current number, so the very next check would wrongly report a duplicate. Checking first keeps the logic correct. |
Watching the code run by hand makes it stick. Let us trace nums = [1, 2, 3, 1]. We follow the set as it grows.
| Step | num | Is num in set? | Set before | Action |
|---|---|---|---|---|
| 1 | 1 | No | { } (empty) | Add 1 to the set |
| 2 | 2 | No | { 1 } | Add 2 to the set |
| 3 | 3 | No | { 1, 2 } | Add 3 to the set |
| 4 | 1 | Yes | { 1, 2, 3 } | Duplicate! Return true |
Look at step four closely. We reach the second 1. The set already holds a 1 from step one. So the contains check returns true, and we stop right there. We never need to touch anything past that point.

The sketch above shows the same trace. The array sits on top, the growing set sits on the right, and the arrow shows how the second 1 finds its earlier twin. Many people find this picture easier than the table, so use whichever clicks for you.
All three approaches give the correct answer. They just pay different prices. Here is the plain comparison.
| Approach | Time | Space | Note |
|---|---|---|---|
| Brute force | O(n squared) | O(1) | Simple but slow on big inputs |
| Sorting | O(n log n) | O(1) or O(n) | Reorders the original array |
| HashSet | O(n) | O(n) | Fastest, uses extra memory |
In an interview, mention brute force first to show you understand the problem. Then bring up sorting as a smarter option. Finally, reach for the HashSet to show you can push to the best time complexity. That path from slow to fast is exactly what interviewers want to see.
| 💡 Interview Insight If the interviewer says memory is tight, pivot to the sorting approach. It uses no extra set and still beats brute force. Naming that trade-out loud signals real judgment, not just memorized Big-O. |
A few small traps catch beginners on this problem. Keep these in mind.
Arrays.sort changes the original array order. Copy the array first if the caller still needs it.contains before add. Swapping the order breaks the logic.A: Contains Duplicate asks you to return true if any value in an array appears more than once, and false if every value is unique. You return a boolean, not an index.
A: The HashSet approach is fastest. You walk the array once and check if each number is already in the set before adding it. This runs in O(n) time because each lookup is constant on average.
A: You sort the array first, which places equal values next to each other. Then you compare each number with its previous neighbor. It runs in O(n log n) time but changes the original array order.
A: Brute force uses two nested loops, so it runs in O(n squared) time and O(1) space. It is simple and correct, but it slows down fast as the array grows.
A: If you add the number first, the set already contains it, so the next check would wrongly report a duplicate. Checking with contains before add keeps the logic correct.
The Contains Duplicate problem in Java looks like a warm-up, and it is. But the HashSet trick inside it is a real skill. You will reuse that same remember-as-you-go idea in many tougher array and string problems.
So take the lesson, not just the answer. Try brute force first to be safe. Reach for sorting when memory is tight. Then use a HashSet when you want the fastest single pass. Once that pattern feels natural, a whole family of problems opens up for you.
You now have Contains Duplicate in your toolkit, so let us keep the momentum going. Here is where you came from and where you head next in this series.