Software Engineer's Blog

Java Set Explained (HashSet vs LinkedHashSet vs TreeSet)

Java Set Explained (HashSet vs LinkedHashSet vs TreeSet)

And How It Compares to Python

If you’ve worked with Java for a while, you’ve probably used a Set to remove duplicates.

But when you actually sit down to write code, a common question comes up:

Should I use HashSet, LinkedHashSet, or TreeSet?

They may look similar at first glance, but they behave quite differently under the hood.
Understanding those differences helps you make better decisions in real-world code.

Set (Interface)

Set is not a class — it’s an interface that defines the contract for all Set implementations.

The key rule is simple:

  • No duplicate elements allowed
  • At most one null is permitted (depending on the implementation)

You don’t create a Set directly.
Instead, you use one of its implementations like HashSet, LinkedHashSet, or TreeSet.

How Set Detects Duplicates

One important thing to keep in mind:

A Set does not determine duplicates by simple value comparison.

It relies on:

  • hashCode()
  • equals()

If these are not implemented correctly, a Set may allow what looks like duplicate data.

For example, two objects with identical fields can still be treated as different if equals() and hashCode() are not overridden properly.

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return age == person.age &&
               Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

public class Main {
    public static void main(String[] args) {
        Set<Person> set = new HashSet<>();

        set.add(new Person("Alice", 30));
        set.add(new Person("Alice", 30)); // 같은 데이터

        System.out.println(set.size());
    }
}

HashSet (Default Choice)

This is the one you’ll use most of the time.

  • Internal structure: Hash table (backed by HashMap)
  • Order: Not guaranteed
  • Performance:
    • Average: O(1)
    • Worst case: O(n) (due to hash collisions)

Use HashSet when:

  • Order doesn’t matter
  • You want the best performance for insert, lookup, and delete

LinkedHashSet (When Order Matters)

Think of it as a HashSet that also keeps track of insertion order.

  • Internal structure: Hash table + linked list
  • Order: Preserves insertion order
  • Performance: Slightly slower than HashSet, but still O(1) on average

Use LinkedHashSet when:

  • You need uniqueness
  • You also need to keep the original order

Typical examples:

  • Recent search history
  • Deduplicated lists that still need to be displayed in order

TreeSet (Always Sorted)

TreeSet keeps elements sorted at all times.

  • Internal structure: Red-Black Tree
  • Order: Sorted (natural order or custom comparator)
  • Performance: O(log n)

A few things to keep in mind:

  • Elements must be comparable
    • Either implement Comparable
    • Or provide a Comparator
  • null is not allowed (because elements must be compared)

Use TreeSet when:

  • You need data to stay sorted
  • You need range-based operations (e.g., values between 10 and 50)

Quick Comparison

JavaPython EquivalentNotes
HashSetsetDefault, fast, unordered
LinkedHashSet(No direct equivalent)Use dict (Python 3.7+)
TreeSet(No direct equivalent)Use sorted() or libraries
frozensetImmutable set (Python only)

Which One Should You Use?

A simple rule of thumb:

  1. Start with HashSet
  2. Switch to LinkedHashSet if order becomes important
  3. Use TreeSet if you need sorted data or range queries

Java vs Python: A Different Approach

Java encourages you to choose the right data structure upfront.

Python takes a more flexible approach:

  • Start simple
  • Adjust only when necessary

Mapping Java to Python

JavaPython EquivalentNotes
HashSetsetDefault, fast, unordered
LinkedHashSet(No direct equivalent)Use dict (Python 3.7+)
TreeSet(No direct equivalent)Use sorted() or libraries
frozensetImmutable set (Python only)

① HashSet ↔ Python set

Python’s set is very similar to HashSet.

my_set = {1, 2, 3}
  • Based on hashing
  • Very fast
  • Does not guarantee order

The order may look stable in some cases, but it’s not something you can rely on.

② LinkedHashSet ↔ Using dict

Python doesn’t have a built-in ordered set.

However, since Python 3.7, dict preserves insertion order.
That makes it a simple way to mimic LinkedHashSet.

my_list = [3, 1, 2, 2, 1]
ordered = list(dict.fromkeys(my_list))

print(ordered)  # [3, 1, 2]
  • Removes duplicates
  • Preserves order

③ TreeSet ↔ Using sorted()

Python doesn’t keep data sorted automatically.

Instead, it sorts when needed:

sorted_values = sorted(my_set)

There’s an important difference here:

  • Java TreeSet → Always sorted (O(log n) per insert)
  • Python sorted() → Sorts when called (O(n log n))

If you need frequent sorting, this difference can matter.

Advanced Note: bisect

If you want to maintain a sorted structure in Python:

  • The bisect module helps find insertion points using binary search
  • But inserting into a list is still O(n)

For large datasets, you may want to look into external libraries like sortedcontainers.

Python-Only Feature: frozenset

Python also provides frozenset, which Java doesn’t have.

fs = frozenset([1, 2, 3])
  • Immutable (cannot be modified)
  • Hashable
  • Can be used as:
    • Dictionary keys
    • Elements inside another set

Final Thoughts

Java and Python solve the same problem in slightly different ways.

  • Java gives you more control by offering multiple specialized implementations
  • Python keeps things simple and lets you adapt when needed

Understanding both approaches makes it easier to choose the right tool depending on the situation.