Hook: Interviewers love this question because both classes are “sets,” but they solve different problems: one is built for speed, the other for sorting.
Question: What is the difference between HashSet and TreeSet in Java?
Answer: HashSet stores unique elements and gives fast average-time operations, but it does not keep elements in any order. TreeSet also stores unique elements, but it keeps them sorted either by natural order or by a provided Comparator (a rule for comparing objects). Because of that sorting, TreeSet is slower than HashSet for add/search/remove.
Interview-Ready Answer: “I use HashSet when I want the fastest set operations and I do not care about order. I use TreeSet when I need the elements kept in sorted order or I need range queries like first, last, or ‘all values between A and B’. The trade-off is performance: HashSet is usually O(1) on average, while TreeSet is O(log n) because it is backed by a balanced tree.”
HashSet is a set implementation backed by a HashMap (a hash table that uses a hash code to find buckets quickly). TreeSet is a set implementation backed by a TreeMap, which uses a TreeMap’s red-black tree (a self-balancing binary search tree that keeps lookup paths short).
HashSet works under the hoodadd(x).hashCode() to pick a bucket.equals() to avoid duplicates.Important detail: in modern Java, heavily-collided buckets can be treeified, so the worst collision path is better than old versions. Still, the usual interview answer is O(1) average for HashSet.
TreeSet works under the hoodadd(x).x with existing elements using natural ordering (Comparable) or a supplied Comparator.O(log n).A subtle point: in a TreeSet, two values are considered “the same” if the comparator says they compare as zero. That means uniqueness is based on comparison, not just equals().
HashSet when you want fast membership checks like “Have I seen this ID before?”TreeSet when you need sorted output, the smallest/largest element, or range queries such as subSet, headSet, and tailSet.LinkedHashSet.| Feature | HashSet | TreeSet |
|---|---|---|
| Ordering | No order | Sorted order |
| Backed by | HashMap | TreeMap |
| Average add/contains/remove | O(1) | O(log n) |
| Null handling | Allows one null | Usually no null |
| Extra features | None for sorting | Range and navigation |
| Best for | Fast lookups | Sorted data |
HashSet: average O(1); memory is a bit higher because the hash table uses buckets and resizing. The default load factor is 0.75, which means it grows when about 75% full.TreeSet: O(log n) for add/remove/contains. For 1,000,000 items, that means roughly around 20 comparison steps instead of scanning everything.HashSet is often a big win.HashSet uses both hashCode() and equals(), so if you override one, you should override the other too.TreeSet uses ordering. If your Comparator is inconsistent with equals(), you can get surprising duplicates or missing elements.TreeSet typically rejects null because it must compare elements to sort them.Memory Hook: Think of HashSet as a messy locker room with labels for fast finding, and TreeSet as a librarian who alphabetizes every book before shelving it.
Scenario: A checkout service in an e-commerce app receives coupon codes and gift-card IDs during payment.
HashSet to quickly detect duplicate coupon codes in the same request, because it only needs fast “have I seen this?” checks.TreeSet to keep active discount thresholds sorted, so it can quickly find the best applicable rule for an order amount.TreeSet for every incoming coupon check, CPU usage rose and latency increased because every insert had to maintain sorted order.What goes wrong: A developer replaces a HashSet with a TreeSet “because sets should be ordered.” The checkout API starts taking longer under load, and logs show more time spent in comparison methods. Users notice slower payment pages, and the business sees more abandoned carts. The bug is not a crash; it is a performance regression caused by choosing sorted order where only uniqueness was needed.
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class HashSetVsTreeSetDemo {
public static void main(String[] args) {
// HashSet: fast membership checks, no guaranteed order.
Set<String> hashSet = new HashSet<>();
hashSet.add("banana");
hashSet.add("apple");
hashSet.add("orange");
hashSet.add("apple"); // duplicate ignored because sets store unique elements
hashSet.add(null); // HashSet allows one null
System.out.println("HashSet contents: " + hashSet);
System.out.println("HashSet contains 'apple'? " + hashSet.contains("apple"));
System.out.println("HashSet size (duplicate ignored): " + hashSet.size());
// TreeSet: sorted order, but no null because it must compare elements.
TreeSet<String> treeSet = new TreeSet<>();
treeSet.add("banana");
treeSet.add("apple");
treeSet.add("orange");
treeSet.add("apple"); // duplicate ignored
System.out.println("TreeSet contents: " + treeSet);
System.out.println("TreeSet first element: " + treeSet.first());
System.out.println("TreeSet last element: " + treeSet.last());
System.out.println("TreeSet subset [apple, orange): " + treeSet.subSet("apple", "orange"));
// Edge case: null in TreeSet throws NullPointerException in the natural-order case.
try {
treeSet.add(null);
System.out.println("Unexpected: null was added to TreeSet");
} catch (NullPointerException e) {
System.out.println("TreeSet rejected null as expected: " + e);
}
// Another edge case: a custom Comparator can change sorting rules.
TreeSet<String> byLengthThenAlphabetical = new TreeSet<>(
Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder())
);
byLengthThenAlphabetical.addAll(Arrays.asList("pear", "kiwi", "apple", "fig", "banana"));
System.out.println("Custom-ordered TreeSet: " + byLengthThenAlphabetical);
// Note: with TreeSet, "equal" means the comparator returns 0.
// If a comparator only compared by length, "pear" and "kiwi" could be treated as duplicates.
TreeSet<String> lengthOnly = new TreeSet<>(Comparator.comparingInt(String::length));
lengthOnly.add("pear");
lengthOnly.add("kiwi"); // same length, comparator returns 0, so this may be treated as a duplicate
System.out.println("Length-only TreeSet (surprising duplicate behavior): " + lengthOnly);
}
}Follow-up & Tricky Questions:
HashSet decide uniqueness? It uses hashCode() to find a bucket and then equals() to check whether the element is already present.TreeSet slower than HashSet? Because every operation must walk a sorted tree and keep it balanced, which costs O(log n) instead of average O(1).TreeSet store custom objects? Yes, if the objects implement Comparable or you supply a Comparator that knows how to order them.LinkedHashSet, HashSet, and TreeSet? LinkedHashSet preserves insertion order, HashSet gives no order, and TreeSet keeps sorted order.TreeSet? Navigation methods such as first(), last(), lower(), higher(), floor(), ceiling(), plus range views like subSet().equals() says they are not, what happens? In TreeSet, the comparator wins for set membership, so one of those values may be silently dropped as a duplicate.HashSet keep insertion order if the hash values happen to be sorted? No. Any apparent order is accidental and should not be relied on.TreeSet contain null? With natural ordering, no—because it needs to compare elements. A custom comparator could theoretically handle nulls, but you should be explicit and careful.Common Mistakes:
TreeSet when only uniqueness is needed. Correction: choose HashSet for faster average performance.HashSet has stable order. Correction: it is unordered; if you need predictable iteration order, use LinkedHashSet.TreeSet, make sure your comparator defines a total order and does not accidentally collapse different values.equals() with comparison. Correction: HashSet relies on equals() plus hashCode(), while TreeSet relies on ordering.Memory Hook: Hash = fast, Tree = sorted. Or picture it as: HashSet is a quick drawer, TreeSet is an alphabetized shelf.
Cheat Sheet:
HashSet = unique elements, no order, average O(1).TreeSet = unique elements, sorted order, O(log n).HashSet allows one null; TreeSet usually does not.TreeSet supports range and navigation operations.HashSet for speed; use TreeSet for sorted views.Practice Tasks:
HashSet and TreeSet to see the order difference.hashCode() and equals(), then store it in a HashSet.TreeSet with a custom Comparator and test what happens when the comparator returns 0 for different objects.