Working with TreeSet and Sorted Collections in Java
Course: Core Java Training
Topic: Working with TreeSet and Sorted Collections
Course Link: Core Java Training Course
Demo Link: Register for a Free Course Demo
Website: JustAcademy Official Website
1. Introduction to TreeSet and Sorted Collections
Java provides the Collections Framework to store, organize, search, and manipulate groups of objects. When an application requires unique elements arranged in sorted order, the TreeSet class is a useful collection implementation.
A TreeSet automatically maintains its elements in ascending natural order by default. It can also use a custom sorting rule supplied through the Comparator interface. Unlike a basic list, it does not allow duplicate elements according to its comparison rules.
Sorted collections are useful when working with unique employee IDs, product prices, student marks, names, rankings, dates, or other values that must remain ordered while elements are inserted and removed.
2. Learning Objectives
- Understand the purpose of the Java Collections Framework and sorted collections.
- Learn the relationship between
Set, SortedSet, NavigableSet, and TreeSet.
- Create and initialize a TreeSet.
- Insert, retrieve, search, and remove elements.
- Sort values using natural ordering and custom comparators.
- Use methods such as
first(), last(), headSet(), and tailSet().
- Understand TreeSet performance, limitations, and common errors.
3. What Is a Sorted Collection?
A sorted collection is a collection that maintains elements according to a defined ordering rule. The ordering may be the natural ordering of elements or a custom order defined by a comparator.
For example, if numbers are inserted in the order 50, 10, 30, and 20 into a TreeSet, the collection maintains the order 10, 20, 30, 50.
Benefits of Sorted Collections
- Elements are available in a predictable sorted order.
- Duplicate values are not retained in a Set implementation.
- Minimum and maximum elements can be accessed conveniently.
- Range-based operations are supported by sorted and navigable sets.
- Applications can search for nearby higher or lower values.
4. Understanding the Set Interface
The Set interface is part of java.util. It represents a collection that does not permit duplicate elements according to its equality rules.
Common Set implementations include:
| Implementation |
Main Characteristics |
Typical Use |
HashSet |
Does not guarantee iteration order. |
Fast membership checks when order is unnecessary. |
LinkedHashSet |
Preserves insertion order. |
Unique elements in the order they were added. |
TreeSet |
Maintains sorted order and rejects comparison-equivalent duplicates. |
Unique elements with automatic sorting. |
5. Understanding SortedSet and NavigableSet
SortedSet extends the Set interface and provides operations for collections whose elements follow a defined order. NavigableSet extends SortedSet with additional methods for finding nearby elements and navigating in either direction.
Collection Interface Hierarchy
Iterable
|
Collection
|
Set
|
SortedSet
|
NavigableSet
|
TreeSet (implements NavigableSet)
Important Interfaces
| Interface |
Purpose |
Examples of Features |
Collection |
Defines common collection operations. |
add(), remove(), size() |
Set |
Represents a collection without duplicate elements. |
contains(), add() |
SortedSet |
Maintains elements in a defined sorted order. |
first(), last(), subSet() |
NavigableSet |
Adds navigation and reverse-order operations. |
lower(), higher(), floor(), ceiling() |
6. What Is TreeSet in Java?
TreeSet is a class in the java.util package that implements NavigableSet. It is backed by a tree-based map structure and maintains elements according to natural ordering or a supplied comparator.
Main Features of TreeSet
- Stores unique elements.
- Maintains sorted order automatically.
- Supports natural ordering and custom comparators.
- Provides navigation and range-view methods.
- Typically provides logarithmic time complexity for basic add, remove, and contains operations.
- Is not synchronized by default.
- Does not accept null elements when using natural ordering.
7. Creating a TreeSet
To use TreeSet, import it from the java.util package.
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
System.out.println(numbers);
}
}
Output:
[]
The statement new TreeSet<>() creates an empty TreeSet that uses the natural ordering of its elements.
Common TreeSet Constructors
| Constructor |
Description |
TreeSet<>() |
Creates an empty set using natural ordering. |
TreeSet<>(Comparator) |
Creates an empty set using a specified comparator. |
TreeSet<>(Collection) |
Creates a set containing collection elements in natural order. |
TreeSet<>(SortedSet) |
Creates a set containing the elements and ordering of another SortedSet. |
8. Adding Elements to TreeSet
The add() method inserts an element into the set if it is not already represented in the collection according to its ordering rules.
import java.util.TreeSet;
public class AddElementsExample {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(50);
numbers.add(10);
numbers.add(40);
numbers.add(20);
numbers.add(30);
System.out.println(numbers);
}
}
Output:
[10, 20, 30, 40, 50]
Although the values were inserted in a different order, TreeSet displays them in ascending order.
Adding Duplicate Elements
import java.util.TreeSet;
public class DuplicateExample {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
System.out.println(numbers.add(10));
System.out.println(numbers.add(20));
System.out.println(numbers.add(10));
System.out.println(numbers);
}
}
Output:
true
true
false
[10, 20]
The third call returns false because the set already contains a value that compares as equal to 10.
9. Natural Ordering in TreeSet
Natural ordering is the default order used by TreeSet when no comparator is supplied. It depends on the element type's implementation of Comparable.
Integer: numeric ascending order.
String: lexicographic order based on Java's string comparison rules.
Double: numeric ascending order, with ordering behavior defined by its comparison implementation.
LocalDate: chronological order.
Example: Sorting Strings
import java.util.TreeSet;
public class StringSortingExample {
public static void main(String[] args) {
TreeSet<String> names = new TreeSet<>();
names.add("Ravi");
names.add("Amit");
names.add("Priya");
names.add("Neha");
System.out.println(names);
}
}
Output:
[Amit, Neha, Priya, Ravi]
String ordering is case-sensitive. Uppercase and lowercase strings may appear in an order that differs from dictionary-style expectations.
10. Retrieving the First and Last Elements
TreeSet provides methods for retrieving the smallest and largest elements in its current ordering.
| Method |
Purpose |
Behavior on an Empty Set |
first() |
Returns the lowest element. |
Throws NoSuchElementException. |
last() |
Returns the highest element. |
Throws NoSuchElementException. |
pollFirst() |
Returns and removes the lowest element. |
Returns null. |
pollLast() |
Returns and removes the highest element. |
Returns null. |
import java.util.TreeSet;
public class FirstLastExample {
public static void main(String[] args) {
TreeSet<Integer> scores = new TreeSet<>();
scores.add(75);
scores.add(45);
scores.add(90);
scores.add(60);
System.out.println("Lowest score: " + scores.first());
System.out.println("Highest score: " + scores.last());
}
}
Output:
Lowest score: 45
Highest score: 90
11. Searching for Elements
The contains() method checks whether an element is present. The result is a Boolean value.
import java.util.TreeSet;
public class SearchExample {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
System.out.println(numbers.contains(20));
System.out.println(numbers.contains(50));
}
}
Output:
true
false
Because TreeSet maintains a sorted structure, its basic membership check typically has logarithmic time complexity.
12. Removing Elements from TreeSet
The remove() method removes a matching element if present. The clear() method removes all elements.
import java.util.TreeSet;
public class RemoveExample {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.remove(20);
System.out.println(numbers);
numbers.clear();
System.out.println(numbers);
}
}
Output:
[10, 30, 40]
[]
13. Iterating Through a TreeSet
The default iterator visits elements in ascending order. A descending iterator visits elements in reverse order.
import java.util.Iterator;
import java.util.TreeSet;
public class IterationExample {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(40);
numbers.add(10);
numbers.add(30);
numbers.add(20);
for (Integer number : numbers) {
System.out.println(number);
}
System.out.println("Descending order:");
Iterator<Integer> iterator = numbers.descendingIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
Output:
10
20
30
40
Descending order:
40
30
20
10
14. Sorting in Descending Order with Comparator
A Comparator defines an alternative ordering without requiring the class itself to change its natural ordering. To store integers in descending order, use Collections.reverseOrder().
import java.util.Collections;
import java.util.TreeSet;
public class DescendingSortExample {
public static void main(String[] args) {
TreeSet<Integer> numbers =
new TreeSet<>(Collections.reverseOrder());
numbers.add(10);
numbers.add(50);
numbers.add(30);
numbers.add(20);
System.out.println(numbers);
}
}
Output:
[50, 30, 20, 10]
The comparator controls both the displayed ordering and how TreeSet determines whether two elements occupy the same ordering position.
15. Custom Sorting Using a Lambda Comparator
Java lambda expressions make it convenient to create a custom comparator. For example, a collection of strings can be ordered by length.
import java.util.Comparator;
import java.util.TreeSet;
public class StringLengthExample {
public static void main(String[] args) {
Comparator<String> byLength =
Comparator.comparingInt(String::length)
.thenComparing(Comparator.naturalOrder());
TreeSet<String> words = new TreeSet<>(byLength);
words.add("Java");
words.add("Programming");
words.add("Code");
words.add("AI");
System.out.println(words);
}
}
Output:
[AI, Code, Java, Programming]
The primary ordering is string length. The secondary ordering sorts strings alphabetically when their lengths are equal. Adding a tie-breaker is important because a comparator that compares only lengths would treat different strings of equal length as equivalent and retain only one of them.
16. Understanding Comparable and Comparator
TreeSet needs a reliable way to compare elements. Java commonly provides two approaches.
| Feature |
Comparable |
Comparator |
| Package |
java.lang |
java.util |
| Method |
compareTo() |
compare() |
| Ordering |
Defines a class's natural ordering. |
Defines an external or alternative ordering. |
| Typical use |
Default ordering for a type. |
Sort by different fields or directions. |
Example: Sorting Custom Objects with Comparable
import java.util.TreeSet;
class Student implements Comparable<Student> {
private int id;
private String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
@Override
public int compareTo(Student other) {
return Integer.compare(this.id, other.id);
}
@Override
public String toString() {
return id + " - " + name;
}
}
public class StudentSortingExample {
public static void main(String[] args) {
TreeSet<Student> students = new TreeSet<>();
students.add(new Student(103, "Priya"));
students.add(new Student(101, "Amit"));
students.add(new Student(102, "Neha"));
System.out.println(students);
}
}
Output:
[101 - Amit, 102 - Neha, 103 - Priya]
The compareTo() method compares student IDs, so students are ordered by ID. If two distinct Student objects have the same ID, this comparison returns zero and TreeSet treats them as equivalent for set membership. If both students must be retained, define an ordering that distinguishes them, such as ID followed by name.
17. Working with headSet(), tailSet(), and subSet()
SortedSet supports range-based views. These methods are helpful when only a portion of the ordered data is needed.
| Method |
Meaning |
Default Boundary Behavior |
headSet(value) |
Elements smaller than the specified value. |
Upper boundary excluded. |
tailSet(value) |
Elements greater than or equal to the specified value. |
Lower boundary included. |
subSet(from, to) |
Elements in the specified range. |
Start included, end excluded. |
import java.util.TreeSet;
public class RangeExample {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
for (int i = 10; i <= 60; i += 10) {
numbers.add(i);
}
System.out.println(numbers.headSet(40));
System.out.println(numbers.tailSet(40));
System.out.println(numbers.subSet(20, 50));
}
}
Output:
[10, 20, 30]
[40, 50, 60]
[20, 30, 40]
These methods return backed views rather than independent copies. Changes made through a view can affect the original TreeSet, and changes to the original may be reflected in the view. Adding a value outside the view's permitted range throws an IllegalArgumentException.
18. Using NavigableSet Methods
Because TreeSet implements NavigableSet, it supports methods for finding elements near a target value.
| Method |
Description |
lower(e) |
Returns the greatest element strictly less than e. |
floor(e) |
Returns the greatest element less than or equal to e. |
ceiling(e) |
Returns the least element greater than or equal to e. |
higher(e) |
Returns the least element strictly greater than e. |
descendingSet() |
Provides a reverse-order view of the set. |
import java.util.TreeSet;
public class NavigationExample {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
System.out.println("Lower than 30: " + numbers.lower(30));
System.out.println("Floor of 30: " + numbers.floor(30));
System.out.println("Ceiling of 25: " + numbers.ceiling(25));
System.out.println("Higher than 30: " + numbers.higher(30));
}
}
Output:
Lower than 30: 20
Floor of 30: 30
Ceiling of 25: 30
Higher than 30: 40
These methods return null when no matching element exists on the requested side of the ordering.
19. Creating a TreeSet from Another Collection
A TreeSet can be created from an existing collection. The constructor copies the collection's elements into a new set and sorts them according to natural ordering.
import java.util.ArrayList;
import java.util.TreeSet;
public class CollectionConversionExample {
public static void main(String[] args) {
ArrayList<Integer> values = new ArrayList<>();
values.add(40);
values.add(10);
values.add(30);
values.add(10);
values.add(20);
TreeSet<Integer> sortedValues = new TreeSet<>(values);
System.out.println("Original list: " + values);
System.out.println("Sorted set: " + sortedValues);
}
}
Output:
Original list: [40, 10, 30, 10, 20]
Sorted set: [10, 20, 30, 40]
The original ArrayList remains unchanged. The TreeSet is a separate collection that removes duplicates and maintains sorted order.
20. Handling Null Values
A TreeSet using natural ordering generally rejects null values because it cannot compare null with ordinary elements. Attempting to insert null into such a set throws a NullPointerException.
import java.util.TreeSet;
public class NullExample {
public static void main(String[] args) {
TreeSet<String> names = new TreeSet<>();
names.add("Amit");
names.add("Neha");
// This throws NullPointerException:
// names.add(null);
System.out.println(names);
}
}
Output:
[Amit, Neha]
Some custom comparators can define a null ordering, but null support depends on the comparator and should be used deliberately. For most applications, avoiding null elements makes collection behavior easier to understand.
21. TreeSet Performance and Time Complexity
TreeSet is designed to keep elements sorted while supporting efficient updates and searches.
| Operation |
Typical Time Complexity |
add() |
O(log n) |
remove() |
O(log n) |
contains() |
O(log n) |
first() / last() |
Typically O(log n) or better depending on implementation state; these are efficient endpoint operations. |
| Iterating through all elements |
O(n) |
Here, n represents the number of elements in the collection. Actual performance depends on the operation and implementation details, but TreeSet's basic add, remove, and search operations have guaranteed logarithmic time costs.
22. TreeSet vs HashSet vs LinkedHashSet
| Feature |
TreeSet |
HashSet |
LinkedHashSet |
| Duplicates |
Not retained |
Not retained |
Not retained |
| Ordering |
Sorted |
No guaranteed iteration order |
Insertion order |
| Typical basic-operation performance |
O(log n) |
Expected O(1) for common operations under suitable hashing conditions |
Expected O(1) for common operations under suitable hashing conditions |
| Custom ordering |
Yes, with Comparator |
No sorted-order support |
No sorted-order support |
| Range queries |
Supported |
Not directly supported |
Not directly supported |
| Best use |
Unique, sorted values |
Unique values without ordering requirements |
Unique values in insertion order |
23. Practical Example: Managing Unique Product Prices
Suppose an application needs to store unique product prices and quickly find the lowest and highest prices. TreeSet can automatically maintain prices in ascending order.
import java.util.TreeSet;
public class ProductPriceExample {
public static void main(String[] args) {
TreeSet<Double> prices = new TreeSet<>();
prices.add(499.99);
prices.add(999.99);
prices.add(299.99);
prices.add(499.99);
prices.add(1499.99);
System.out.println("All unique prices: " + prices);
System.out.println("Lowest price: " + prices.first());
System.out.println("Highest price: " + prices.last());
System.out.println("Price at or above 700: " + prices.ceiling(700.0));
}
}
Output:
All unique prices: [299.99, 499.99, 999.99, 1499.99]
Lowest price: 299.99
Highest price: 1499.99
Price at or above 700: 999.99
This example demonstrates how TreeSet can simplify sorting, duplicate removal, and navigation through ordered values. For financial applications requiring precise decimal arithmetic, consider using BigDecimal rather than double.
24. Common Mistakes and How to Avoid Them
| Mistake |
Why It Happens |
Solution |
| Expecting insertion order |
TreeSet sorts values automatically. |
Use LinkedHashSet if insertion order is required. |
| Expecting duplicates to remain |
Set implementations reject duplicates according to their membership rules. |
Use a List if duplicate entries must be preserved. |
| Adding incompatible element types |
Elements cannot be compared consistently. |
Store mutually comparable elements or supply a suitable Comparator. |
| Inserting null into a naturally ordered set |
Natural ordering cannot compare null with regular elements. |
Avoid null or define a comparator that deliberately supports it. |
| Using an inconsistent comparator |
Different objects may compare as zero despite not being equal. |
Design ordering so comparison is consistent with equals whenever possible. |
| Modifying the set during enhanced for-loop iteration |
Structural changes can invalidate the iterator. |
Use the iterator's supported remove operation or modify the collection outside the iteration. |
| Assuming range methods return independent copies |
headSet(), tailSet(), and subSet() return backed views. |
Create a new TreeSet from the view if an independent copy is required. |
25. Best Practices for Using TreeSet
- Use TreeSet when sorted, unique elements are required.
- Choose HashSet when ordering is unnecessary and expected constant-time membership operations are more important.
- Use LinkedHashSet when insertion order must be preserved.
- Use meaningful, stable comparison rules for custom objects.
- Use
Integer.compare() or comparator utilities rather than subtracting integers to implement comparisons, because subtraction can overflow.
- Include tie-breakers in comparators when distinct values must remain distinct in the set.
- Remember that TreeSet is not thread-safe by default.
- Do not rely on fail-fast iterators as a concurrency-control mechanism.
- Use range views carefully and understand that their modifications can affect the original set.
26. Practice Exercises
- Create a TreeSet of integers and print its values in ascending order.
- Insert duplicate names into a TreeSet and observe which entries remain.
- Create a TreeSet of integers in descending order using
Collections.reverseOrder().
- Store student marks and print the minimum and maximum marks.
- Use
headSet() to display values smaller than 50.
- Use
tailSet() to display values greater than or equal to 50.
- Use
lower(), floor(), ceiling(), and higher() to find values around a target.
- Create a Student class implementing Comparable and sort students by ID.
- Create a custom Comparator that sorts employees by salary and then by employee ID.
- Convert an ArrayList containing duplicate integers into a TreeSet and compare the two collections.
27. Mini Project: Student Marks Organizer
Objective: Build a small program that stores unique student marks in sorted order and provides basic navigation.
Requirements:
- Insert several integer marks into a TreeSet.
- Display the marks in ascending order.
- Print the lowest and highest marks.
- Display all marks below a selected threshold.
- Find the closest mark greater than or equal to a requested value.
- Remove a mark and display the updated collection.
import java.util.TreeSet;
public class StudentMarksOrganizer {
public static void main(String[] args) {
TreeSet<Integer> marks = new TreeSet<>();
marks.add(78);
marks.add(92);
marks.add(65);
marks.add(85);
marks.add(78);
System.out.println("Sorted marks: " + marks);
System.out.println("Lowest mark: " + marks.first());
System.out.println("Highest mark: " + marks.last());
System.out.println("Marks below 85: " + marks.headSet(85));
System.out.println("First mark at least 80: " + marks.ceiling(80));
marks.remove(65);
System.out.println("After removal: " + marks);
}
}
Expected Output:
Sorted marks: [65, 78, 85, 92]
Lowest mark: 65
Highest mark: 92
Marks below 85: [65, 78]
First mark at least 80: 85
After removal: [78, 85, 92]
28. Interview Questions and Answers
Q1. What is TreeSet in Java?
Answer: TreeSet is a Set implementation that maintains unique elements in natural or comparator-defined sorted order. It implements NavigableSet.
Q2. Does TreeSet allow duplicate elements?
Answer: No. Elements that compare as equal under the set's ordering are treated as the same element and are not stored as separate entries.
Q3. What is the difference between SortedSet and NavigableSet?
Answer: SortedSet provides sorted-order and range operations. NavigableSet extends it with methods such as lower, floor, ceiling, higher, and descendingSet.
Q4. How does TreeSet sort elements?
Answer: It uses natural ordering through Comparable by default or a Comparator supplied when the set is created.
Q5. Can TreeSet store custom objects?
Answer: Yes. Custom objects must be mutually comparable through Comparable or a supplied Comparator.
Q6. What happens if two objects compare as equal?
Answer: If the comparison returns zero, TreeSet treats the objects as equivalent for set membership, so it will not retain both as separate elements.
Q7. What is the time complexity of add(), remove(), and contains()?
Answer: These basic operations have guaranteed logarithmic time complexity, O(log n), in TreeSet.
Q8. Does TreeSet preserve insertion order?
Answer: No. It preserves the order defined by its natural ordering or comparator.
Q9. What is the difference between headSet() and tailSet()?
Answer: headSet() returns a view of elements below a boundary, while tailSet() returns a view of elements at or above a boundary by default.
Q10. What is the difference between Comparable and Comparator?
Answer: Comparable defines a type's natural ordering through compareTo(). Comparator defines an external ordering through compare() and allows different sorting strategies.
Q11. Is TreeSet thread-safe?
Answer: No. Concurrent access involving modifications requires appropriate external synchronization or a different concurrent collection design.
Q12. Can TreeSet contain null?
Answer: A TreeSet using natural ordering does not permit null. A custom comparator may support null, depending on its comparison logic.
29. Quick Revision Checklist
- TreeSet stores unique elements in sorted order.
- TreeSet implements NavigableSet and supports SortedSet operations.
- Natural ordering relies on Comparable.
- Custom ordering can be provided through Comparator.
first() and last() retrieve the lowest and highest elements.
headSet(), tailSet(), and subSet() provide range views.
lower(), floor(), ceiling(), and higher() support navigation.
- Basic add, remove, and contains operations have O(log n) time complexity.
- TreeSet does not preserve insertion order and is not synchronized by default.
- Comparison rules should be consistent with equals whenever possible.
30. Conclusion
TreeSet is an important part of the Java Collections Framework when an application needs unique elements maintained in sorted order. Its support for natural ordering, custom comparators, range views, and navigation methods makes it suitable for tasks such as managing scores, prices, IDs, and ordered values.
Understanding the difference between Set, SortedSet, NavigableSet, Comparable, and Comparator helps developers choose the right collection and avoid common sorting and duplicate-handling errors. Practice the examples and exercises in this lesson to build confidence using TreeSet in real Java programs.
To continue learning Java, explore the Core Java Training Course, register for a course demo, or visit the JustAcademy website.