Set in Java: HashSet, LinkedHashSet and TreeSet
A Set stores unique elements. The three implementations differ in ordering and in what uniqueness is based on.
-
Java Basics
- Introduction to Java
- Setting Up Java and Writing Your First Program
- Variables, Data Types and Literals in Java
- Type Casting and Type Conversion in Java
- Operators and Expressions in Java
- Input and Output in Java
- Comments, Keywords and Naming Conventions in Java
- Control Flow in Java: if, else and switch
- Loops in Java: for, while and do-while
- Methods
- Arrays and Strings
-
OOP
- Classes and Objects in Java
- Constructors in Java
- The this Keyword in Java
- The static Keyword in Java
- Encapsulation in Java
- Access Modifiers in Java
- Inheritance in Java
- Method Overriding and super in Java
- Polymorphism in Java
- Abstraction, Abstract Classes and Interfaces in Java
- Composition, Aggregation and Association in Java
- The Object Lifecycle in Java
- Core Java
- Exception Handling
-
Collections
- The Java Collections Framework
- List in Java: ArrayList, LinkedList, Vector and Stack
- Set in Java: HashSet, LinkedHashSet and TreeSet
- Map in Java: HashMap, LinkedHashMap and TreeMap
- How HashMap Works Internally in Java
- Queue and Deque in Java: ArrayDeque and PriorityQueue
- Iterators in Java
- Comparable and Comparator in Java
- Collections Utilities and Choosing the Right Collection
- Generics
- Java 8+
- Stream API
- Date and Time
- File and I/O
-
Multithreading
- Threads in Java: Processes, Runnable and Thread
- Thread Lifecycle in Java
- Synchronization in Java: synchronized and volatile
- Locks and Atomic Classes in Java
- Race Conditions and Deadlocks in Java
- The Executor Framework and Thread Pools in Java
- Future and CompletableFuture in Java
- Concurrent Collections in Java
- The Java Memory Model
- JVM and Memory
- Advanced Java
- Networking
- JDBC
- Testing
What a Set is
A Set is a collection with no duplicates. Adding an element that is already present has no effect and returns false.
Set<String> tags = new HashSet<>();
System.out.println(tags.add("java")); // true
System.out.println(tags.add("java")); // false, already present
tags.addAll(List.of("oop", "collections"));
System.out.println(tags.size()); // 3
System.out.println(tags.contains("oop"));
tags.remove("oop");The three implementations
HashSet | LinkedHashSet | TreeSet | |
|---|---|---|---|
| Ordering | None, and it may change | Insertion order | Sorted |
| Backed by | A hash table | Hash table plus a linked list | A red black tree |
add, remove, contains | O(1) average | O(1) average | O(log n) |
| Uniqueness based on | hashCode and equals | hashCode and equals | compareTo or a Comparator |
Allows null | One | One | No |
| Memory | Lowest | Higher | Higher |
Set<String> hash = new HashSet<>(List.of("delta", "alpha", "charlie"));
Set<String> linked = new LinkedHashSet<>(List.of("delta", "alpha", "charlie"));
Set<String> tree = new TreeSet<>(List.of("delta", "alpha", "charlie"));
System.out.println(hash); // some unspecified order
System.out.println(linked); // [delta, alpha, charlie]
System.out.println(tree); // [alpha, charlie, delta]Uniqueness depends on your class
record Tag(String name) { } // equals and hashCode generated
Set<Tag> tags = new HashSet<>();
tags.add(new Tag("java"));
tags.add(new Tag("java"));
System.out.println(tags.size()); // 1class BadTag { // no equals or hashCode
private final String name;
BadTag(String name) { this.name = name; }
}
Set<BadTag> bad = new HashSet<>();
bad.add(new BadTag("java"));
bad.add(new BadTag("java"));
System.out.println(bad.size()); // 2 - identity based, not value basedAHashSetcan only detect duplicates if the element type definesequalsandhashCode. ATreeSetuses ordering instead, so it needsComparableor aComparator, and it treats a comparison result of zero as a duplicate even whenequalswould disagree.
TreeSet and navigation
NavigableSet<Integer> marks = new TreeSet<>(List.of(35, 48, 62, 79, 91));
System.out.println(marks.first()); // 35
System.out.println(marks.last()); // 91
System.out.println(marks.floor(60)); // 48, greatest at most 60
System.out.println(marks.ceiling(60)); // 62, least at least 60
System.out.println(marks.headSet(62)); // [35, 48]
System.out.println(marks.tailSet(62)); // [62, 79, 91]
System.out.println(marks.subSet(48, 79)); // [48, 62]
System.out.println(marks.descendingSet()); // [91, 79, 62, 48, 35]Set<String> caseInsensitive = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
caseInsensitive.add("Java");
System.out.println(caseInsensitive.add("java")); // false, the comparator says equalLinkedHashSet for deduplication
List<String> withDuplicates = List.of("b", "a", "b", "c", "a");
List<String> unique = new ArrayList<>(new LinkedHashSet<>(withDuplicates));
System.out.println(unique); // [b, a, c] - duplicates gone, order keptSet operations
Set<String> a = new HashSet<>(Set.of("java", "sql", "html"));
Set<String> b = Set.of("sql", "css");
Set<String> union = new HashSet<>(a);
union.addAll(b); // [java, sql, html, css]
Set<String> intersection = new HashSet<>(a);
intersection.retainAll(b); // [sql]
Set<String> difference = new HashSet<>(a);
difference.removeAll(b); // [java, html]Copy first. These methods modify the set they are called on, which is easy to forget.
Mutable elements break sets
class MutableTag {
String name; // used by equals and hashCode
}
MutableTag tag = new MutableTag();
tag.name = "java";
Set<MutableTag> set = new HashSet<>();
set.add(tag);
tag.name = "sql"; // the hash changed
System.out.println(set.contains(tag)); // false, although it is in thereCommon mistakes
- Adding objects without
equalsandhashCodeand getting duplicates. - Relying on
HashSetiteration order. - Putting
nullinto aTreeSet, which throws. - Using a comparator inconsistent with
equals, so elements vanish unexpectedly. - Mutating an element after inserting it.
- Forgetting that
retainAllandremoveAllmodify in place.
Best practices
- Use
HashSetby default,LinkedHashSetwhen order must be predictable,TreeSetwhen sorting or range queries are needed. - Make set elements immutable, ideally records.
- Use
Set.of(...)for fixed sets. - Use
EnumSetfor enum elements. - Use a
Setfor membership tests rather thanList.contains, which is linear.
Practice
- Add two equal records to a
HashSetand explain the size, then removehashCodeand explain it again. - Remove duplicates from a list while preserving order, in one line.
- Use a
TreeSetto find the highest mark not exceeding 70. - Why does
TreeSetrejectnullwhileHashSetaccepts one? - Compute the union, intersection and difference of two sets without modifying either.
Conclusion
A set gives you uniqueness, and how it decides uniqueness depends on the implementation: hashing and equals for the hash based sets, ordering for TreeSet. Keep elements immutable and the rest follows.