Java - Creating and Using TreeSet

 Creating and Using TreeSet in Java

Introduction to Java TreeSet

Java TreeSet is one of the most important data structures in the Java Collections Framework, and it is widely used when programmers need a sorted, unique, and high-performance set implementation. TreeSet internally uses a Red-Black Tree (a type of self-balancing binary search tree) to store elements in a natural sorted order or in a custom order defined by a Comparator. Because TreeSet maintains sorting automatically, it becomes a powerful tool for scenarios like ranking, leaderboards, directory structures, maintaining sorted lists, and performing range search operations. TreeSet also ensures that duplicate values are never stored, making it useful for tasks requiring uniqueness with ordering. TreeSet allows fast operations like searching, inserting, and deleting with O(log n) performance due to its tree-based structure. Programmers prefer TreeSet when ordering is essential, making it different from HashSet which is based on hashing and does not maintain order. TreeSet is a key component for writing efficient, readable, and optimized Java applications where sorted data is a priority.

Features and Characteristics of TreeSet

TreeSet comes with several essential features that make it a widely-used class in Java. First, it maintains automatic sorting of elements either in natural order or by a custom comparator. This eliminates the need for manual sorting later. Second, TreeSet does not allow null values because comparing null with other elements results in a runtime exception. It ensures uniqueness by preventing duplicate data entry, enabling developers to maintain clean and predictable datasets. TreeSet is implemented using a Red-Black Tree structure, which maintains balance and ensures O(log n) time complexity for major operations like add(), remove(), and contains(). It also provides rich navigation methods such as first(), last(), ceiling(), floor(), lower(), and higher() for advanced querying. TreeSet is ideal when the application requires both uniqueness and sorted ordering, making it perfect for ordered sets, alphabetically sorted names, numeric sequences, and priority-based operations.

Creating a TreeSet in Java

Creating a TreeSet in Java is straightforward and can be done with just a few lines of code using the default constructor. When created without arguments, TreeSet automatically sorts elements in their natural ordering. For strings, this means alphabetical order, and for numbers, ascending order. Creating a TreeSet requires importing java.util.TreeSet. After creation, elements can be added using the add() method. Since TreeSet does not allow duplicates, adding an already existing value will simply be ignored, ensuring uniqueness. TreeSet is especially useful in cases where ordering is essential from the start, as it sorts elements automatically when they are inserted, unlike List collections where sorting must be done separately. The creation process is simple yet powerful, enabling developers to implement sorted datasets with ease.

Example: Creating a TreeSet


import java.util.TreeSet;

public class CreateTreeSetExample {
    public static void main(String[] args) {
        TreeSet numbers = new TreeSet<>();

        numbers.add(30);
        numbers.add(10);
        numbers.add(50);
        numbers.add(20);

        System.out.println("TreeSet elements: " + numbers);
    }
}

Output:


TreeSet elements: [10, 20, 30, 50]

Adding Elements to a TreeSet

Adding elements to a TreeSet is done using the add() method, which automatically places the element in the correct position according to the natural sorting order or the comparator provided. When a new element is inserted, the TreeSet checks for duplicates and ignores the entry if it already exists. Adding elements is efficient due to the underlying Red-Black Tree mechanism, which maintains a balanced structure and ensures operations in logarithmic time. While inserting, TreeSet compares values to determine their position, so all inserted elements must be mutually comparable. This is why null values are not permitted, as comparing null throws a NullPointerException. TreeSet supports adding all elements from another collection using addAll(), making it useful for merging sorted sets. Adding elements to TreeSet ensures that the data remains sorted and ready for retrieval without any additional sorting steps, making it extremely efficient for real-time sorted operations.

Example: Adding Elements


import java.util.TreeSet;

public class AddElementsTreeSet {
    public static void main(String[] args) {
        TreeSet names = new TreeSet<>();

        names.add("John");
        names.add("Alex");
        names.add("Bob");
        names.add("Daisy");

        System.out.println("Names in TreeSet: " + names);
    }
}

Output:


Names in TreeSet: [Alex, Bob, Daisy, John]

Accessing TreeSet Elements

TreeSet provides several ways to access elements, including advanced navigation methods. While it does not allow random access like ArrayList, TreeSet offers sorted access through methods like first() to get the smallest element and last() to retrieve the largest element. Using higher() and lower() methods, you can fetch values that are just above or below a given element, providing range-based navigation. Additionally, methods like ceiling() return the nearest greater or equal element, while floor() returns the nearest lower or equal element. These capabilities make TreeSet useful for scenarios like searching nearest values in ranking systems or numeric ranges. Iterators can be used to traverse elements in ascending or descending order using iterator() or descendingIterator(). These methods enable efficient and structured retrieval of sorted data without the need for extra sorting operations.

Example: Accessing Elements


import java.util.TreeSet;

public class AccessElementsTreeSet {
    public static void main(String[] args) {
        TreeSet values = new TreeSet<>();

        values.add(15);
        values.add(5);
        values.add(25);
        values.add(10);

        System.out.println("First Element: " + values.first());
        System.out.println("Last Element: " + values.last());
        System.out.println("Higher than 10: " + values.higher(10));
        System.out.println("Lower than 15: " + values.lower(15));
    }
}

Output:


First Element: 5
Last Element: 25
Higher than 10: 15
Lower than 15: 10

Removing Elements from TreeSet

Removing elements from a TreeSet can be done using several methods such as remove(), pollFirst(), and pollLast(). The remove() method removes a specific element if it exists in the set, and returns true or false depending on the result. The pollFirst() method removes and returns the smallest element, while pollLast() removes and returns the largest element. These operations are efficient because TreeSet maintains its underlying Red-Black Tree structure. Removing elements maintains the sorted order without requiring explicit re-sorting. When deletion occurs, the tree readjusts itself to preserve balance, ensuring that performance remains optimal. Removing elements is useful in cases like priority queues, range-based data filtering, and dynamic sorted structures where items must be removed frequently.

Example: Removing Elements


import java.util.TreeSet;

public class RemoveElementsTreeSet {
    public static void main(String[] args) {
        TreeSet nums = new TreeSet<>();

        nums.add(100);
        nums.add(50);
        nums.add(150);
        nums.add(75);

        nums.remove(50);
        System.out.println("After removing 50: " + nums);

        System.out.println("Removed first: " + nums.pollFirst());
        System.out.println("Removed last: " + nums.pollLast());

        System.out.println("Final TreeSet: " + nums);
    }
}

Output:


After removing 50: [75, 100, 150]
Removed first: 75
Removed last: 150
Final TreeSet: [100]

TreeSet with Custom Comparator

One of the most powerful features of TreeSet is the ability to define custom sorting rules using a Comparator. Instead of relying on natural ordering, TreeSet can sort elements according to any logic provided by the programmer. This is especially useful when sorting complex objects like students, employees, or other custom classes. By using Comparator, you can sort strings in reverse order, sort numbers from highest to lowest, or sort objects based on attributes like age, rank, or salary. When a comparator is passed to the TreeSet constructor, it becomes the defining rule for all future insertions and comparisons. This makes TreeSet a flexible and highly customizable sorted data structure for professional-level coding.

Example: TreeSet with Custom Comparator


import java.util.TreeSet;
import java.util.Comparator;

public class CustomComparatorTreeSet {
    public static void main(String[] args) {

        TreeSet words = new TreeSet<>(Comparator.reverseOrder());

        words.add("Apple");
        words.add("Mango");
        words.add("Banana");
        words.add("Orange");

        System.out.println("TreeSet in Reverse Order: " + words);
    }
}

Output:


TreeSet in Reverse Order: [Orange, Mango, Banana, Apple]

Iterating Over a TreeSet

TreeSet provides multiple ways to iterate through its elements, including Iterator, enhanced for-loop, and the descendingIterator() method. Using an Iterator allows forward traversal in ascending order, while descendingIterator() enables reverse-order traversal. Because TreeSet maintains sorting automatically, iteration always happens in sorted order unless explicitly reversed. Iteration is efficient because TreeSet uses an internal tree structure that stores element references in sorted order. Developers often iterate over TreeSet when they need sorted outputs or must perform sequential operations like printing, filtering, or exporting the data. Iteration does not allow modification of the set while traversing unless using Iterator’s remove() method.

Example: Iterating Through a TreeSet


import java.util.TreeSet;
import java.util.Iterator;

public class IterateTreeSetExample {
    public static void main(String[] args) {
        TreeSet set = new TreeSet<>();

        set.add(3);
        set.add(1);
        set.add(4);
        set.add(2);

        System.out.println("Ascending Order:");
        for (Integer n : set) {
            System.out.println(n);
        }

        System.out.println("Descending Order:");
        Iterator it = set.descendingIterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

Output:


Ascending Order:
1
2
3
4
Descending Order:
4
3
2
1

Difference Between HashSet and TreeSet

Although both HashSet and TreeSet implement the Set interface and store unique elements, they differ significantly in behavior, performance, and internal implementation. TreeSet maintains sorted order while HashSet does not. TreeSet is slower because all operations take O(log n) time due to the Red-Black Tree structure, whereas HashSet operations take average O(1) time because they use hashing. TreeSet does not allow null elements, while HashSet allows one null value. TreeSet is ideal for cases where data must be stored in sorted form or requires range queries, while HashSet is better for fast access without ordering. Also, TreeSet supports navigation methods like first(), last(), higher(), and lower(), which HashSet does not offer. HashSet is better for performance, TreeSet for sorted structure.


TreeSet is a powerful, sorted, and unique-element collection in Java that excels in situations where ordering matters. It provides efficient performance due to its Red-Black Tree structure and supports rich methods for navigation, comparison, and range-based queries. Whether you are working with numbers, strings, or custom objects, TreeSet enables clean and predictable storage of sorted data. Its prohibition of duplicates and automatic organization make it one of the most useful collections in Java for maintaining ordered datasets without additional sorting code. Understanding how TreeSet works, how to add, remove, and iterate elements, and how to create custom comparators is essential for building optimized and professional Java applications.

logo

Java

Beginner 5 Hours

 Creating and Using TreeSet in Java

Introduction to Java TreeSet

Java TreeSet is one of the most important data structures in the Java Collections Framework, and it is widely used when programmers need a sorted, unique, and high-performance set implementation. TreeSet internally uses a Red-Black Tree (a type of self-balancing binary search tree) to store elements in a natural sorted order or in a custom order defined by a Comparator. Because TreeSet maintains sorting automatically, it becomes a powerful tool for scenarios like ranking, leaderboards, directory structures, maintaining sorted lists, and performing range search operations. TreeSet also ensures that duplicate values are never stored, making it useful for tasks requiring uniqueness with ordering. TreeSet allows fast operations like searching, inserting, and deleting with O(log n) performance due to its tree-based structure. Programmers prefer TreeSet when ordering is essential, making it different from HashSet which is based on hashing and does not maintain order. TreeSet is a key component for writing efficient, readable, and optimized Java applications where sorted data is a priority.

Features and Characteristics of TreeSet

TreeSet comes with several essential features that make it a widely-used class in Java. First, it maintains automatic sorting of elements either in natural order or by a custom comparator. This eliminates the need for manual sorting later. Second, TreeSet does not allow null values because comparing null with other elements results in a runtime exception. It ensures uniqueness by preventing duplicate data entry, enabling developers to maintain clean and predictable datasets. TreeSet is implemented using a Red-Black Tree structure, which maintains balance and ensures O(log n) time complexity for major operations like add(), remove(), and contains(). It also provides rich navigation methods such as first(), last(), ceiling(), floor(), lower(), and higher() for advanced querying. TreeSet is ideal when the application requires both uniqueness and sorted ordering, making it perfect for ordered sets, alphabetically sorted names, numeric sequences, and priority-based operations.

Creating a TreeSet in Java

Creating a TreeSet in Java is straightforward and can be done with just a few lines of code using the default constructor. When created without arguments, TreeSet automatically sorts elements in their natural ordering. For strings, this means alphabetical order, and for numbers, ascending order. Creating a TreeSet requires importing java.util.TreeSet. After creation, elements can be added using the add() method. Since TreeSet does not allow duplicates, adding an already existing value will simply be ignored, ensuring uniqueness. TreeSet is especially useful in cases where ordering is essential from the start, as it sorts elements automatically when they are inserted, unlike List collections where sorting must be done separately. The creation process is simple yet powerful, enabling developers to implement sorted datasets with ease.

Example: Creating a TreeSet

import java.util.TreeSet; public class CreateTreeSetExample { public static void main(String[] args) { TreeSet numbers = new TreeSet<>(); numbers.add(30); numbers.add(10); numbers.add(50); numbers.add(20); System.out.println("TreeSet elements: " + numbers); } }

Output:

TreeSet elements: [10, 20, 30, 50]

Adding Elements to a TreeSet

Adding elements to a TreeSet is done using the add() method, which automatically places the element in the correct position according to the natural sorting order or the comparator provided. When a new element is inserted, the TreeSet checks for duplicates and ignores the entry if it already exists. Adding elements is efficient due to the underlying Red-Black Tree mechanism, which maintains a balanced structure and ensures operations in logarithmic time. While inserting, TreeSet compares values to determine their position, so all inserted elements must be mutually comparable. This is why null values are not permitted, as comparing null throws a NullPointerException. TreeSet supports adding all elements from another collection using addAll(), making it useful for merging sorted sets. Adding elements to TreeSet ensures that the data remains sorted and ready for retrieval without any additional sorting steps, making it extremely efficient for real-time sorted operations.

Example: Adding Elements

import java.util.TreeSet; public class AddElementsTreeSet { public static void main(String[] args) { TreeSet names = new TreeSet<>(); names.add("John"); names.add("Alex"); names.add("Bob"); names.add("Daisy"); System.out.println("Names in TreeSet: " + names); } }

Output:

Names in TreeSet: [Alex, Bob, Daisy, John]

Accessing TreeSet Elements

TreeSet provides several ways to access elements, including advanced navigation methods. While it does not allow random access like ArrayList, TreeSet offers sorted access through methods like first() to get the smallest element and last() to retrieve the largest element. Using higher() and lower() methods, you can fetch values that are just above or below a given element, providing range-based navigation. Additionally, methods like ceiling() return the nearest greater or equal element, while floor() returns the nearest lower or equal element. These capabilities make TreeSet useful for scenarios like searching nearest values in ranking systems or numeric ranges. Iterators can be used to traverse elements in ascending or descending order using iterator() or descendingIterator(). These methods enable efficient and structured retrieval of sorted data without the need for extra sorting operations.

Example: Accessing Elements

import java.util.TreeSet; public class AccessElementsTreeSet { public static void main(String[] args) { TreeSet values = new TreeSet<>(); values.add(15); values.add(5); values.add(25); values.add(10); System.out.println("First Element: " + values.first()); System.out.println("Last Element: " + values.last()); System.out.println("Higher than 10: " + values.higher(10)); System.out.println("Lower than 15: " + values.lower(15)); } }

Output:

First Element: 5 Last Element: 25 Higher than 10: 15 Lower than 15: 10

Removing Elements from TreeSet

Removing elements from a TreeSet can be done using several methods such as remove(), pollFirst(), and pollLast(). The remove() method removes a specific element if it exists in the set, and returns true or false depending on the result. The pollFirst() method removes and returns the smallest element, while pollLast() removes and returns the largest element. These operations are efficient because TreeSet maintains its underlying Red-Black Tree structure. Removing elements maintains the sorted order without requiring explicit re-sorting. When deletion occurs, the tree readjusts itself to preserve balance, ensuring that performance remains optimal. Removing elements is useful in cases like priority queues, range-based data filtering, and dynamic sorted structures where items must be removed frequently.

Example: Removing Elements

import java.util.TreeSet; public class RemoveElementsTreeSet { public static void main(String[] args) { TreeSet nums = new TreeSet<>(); nums.add(100); nums.add(50); nums.add(150); nums.add(75); nums.remove(50); System.out.println("After removing 50: " + nums); System.out.println("Removed first: " + nums.pollFirst()); System.out.println("Removed last: " + nums.pollLast()); System.out.println("Final TreeSet: " + nums); } }

Output:

After removing 50: [75, 100, 150] Removed first: 75 Removed last: 150 Final TreeSet: [100]

TreeSet with Custom Comparator

One of the most powerful features of TreeSet is the ability to define custom sorting rules using a Comparator. Instead of relying on natural ordering, TreeSet can sort elements according to any logic provided by the programmer. This is especially useful when sorting complex objects like students, employees, or other custom classes. By using Comparator, you can sort strings in reverse order, sort numbers from highest to lowest, or sort objects based on attributes like age, rank, or salary. When a comparator is passed to the TreeSet constructor, it becomes the defining rule for all future insertions and comparisons. This makes TreeSet a flexible and highly customizable sorted data structure for professional-level coding.

Example: TreeSet with Custom Comparator

import java.util.TreeSet; import java.util.Comparator; public class CustomComparatorTreeSet { public static void main(String[] args) { TreeSet words = new TreeSet<>(Comparator.reverseOrder()); words.add("Apple"); words.add("Mango"); words.add("Banana"); words.add("Orange"); System.out.println("TreeSet in Reverse Order: " + words); } }

Output:

TreeSet in Reverse Order: [Orange, Mango, Banana, Apple]

Iterating Over a TreeSet

TreeSet provides multiple ways to iterate through its elements, including Iterator, enhanced for-loop, and the descendingIterator() method. Using an Iterator allows forward traversal in ascending order, while descendingIterator() enables reverse-order traversal. Because TreeSet maintains sorting automatically, iteration always happens in sorted order unless explicitly reversed. Iteration is efficient because TreeSet uses an internal tree structure that stores element references in sorted order. Developers often iterate over TreeSet when they need sorted outputs or must perform sequential operations like printing, filtering, or exporting the data. Iteration does not allow modification of the set while traversing unless using Iterator’s remove() method.

Example: Iterating Through a TreeSet

import java.util.TreeSet; import java.util.Iterator; public class IterateTreeSetExample { public static void main(String[] args) { TreeSet set = new TreeSet<>(); set.add(3); set.add(1); set.add(4); set.add(2); System.out.println("Ascending Order:"); for (Integer n : set) { System.out.println(n); } System.out.println("Descending Order:"); Iterator it = set.descendingIterator(); while (it.hasNext()) { System.out.println(it.next()); } } }

Output:

Ascending Order: 1 2 3 4 Descending Order: 4 3 2 1

Difference Between HashSet and TreeSet

Although both HashSet and TreeSet implement the Set interface and store unique elements, they differ significantly in behavior, performance, and internal implementation. TreeSet maintains sorted order while HashSet does not. TreeSet is slower because all operations take O(log n) time due to the Red-Black Tree structure, whereas HashSet operations take average O(1) time because they use hashing. TreeSet does not allow null elements, while HashSet allows one null value. TreeSet is ideal for cases where data must be stored in sorted form or requires range queries, while HashSet is better for fast access without ordering. Also, TreeSet supports navigation methods like first(), last(), higher(), and lower(), which HashSet does not offer. HashSet is better for performance, TreeSet for sorted structure.


TreeSet is a powerful, sorted, and unique-element collection in Java that excels in situations where ordering matters. It provides efficient performance due to its Red-Black Tree structure and supports rich methods for navigation, comparison, and range-based queries. Whether you are working with numbers, strings, or custom objects, TreeSet enables clean and predictable storage of sorted data. Its prohibition of duplicates and automatic organization make it one of the most useful collections in Java for maintaining ordered datasets without additional sorting code. Understanding how TreeSet works, how to add, remove, and iterate elements, and how to create custom comparators is essential for building optimized and professional Java applications.

Related Tutorials

Frequently Asked Questions for Java

Java is known for its key features such as object-oriented programming, platform independence, robust exception handling, multithreading capabilities, and automatic garbage collection.

The Java Development Kit (JDK) is a software development kit used to develop Java applications. The Java Runtime Environment (JRE) provides libraries and other resources to run Java applications, while the Java Virtual Machine (JVM) executes Java bytecode.

Java is a high-level, object-oriented programming language known for its platform independence. This means that Java programs can run on any device that has a Java Virtual Machine (JVM) installed, making it versatile across different operating systems.

Deadlock is a situation in multithreading where two or more threads are blocked forever, waiting for each other to release resources.

Functional programming in Java involves writing code using functions, immutability, and higher-order functions, often utilizing features introduced in Java 8.

A process is an independent program in execution, while a thread is a lightweight subprocess that shares resources with other threads within the same process.

The Comparable interface defines a natural ordering for objects, while the Comparator interface defines an external ordering.

The List interface allows duplicate elements and maintains the order of insertion, while the Set interface does not allow duplicates and does not guarantee any specific order.

String is immutable, meaning its value cannot be changed after creation. StringBuffer and StringBuilder are mutable, allowing modifications to their contents. The main difference between them is that StringBuffer is synchronized, making it thread-safe, while StringBuilder is not.

Checked exceptions are exceptions that must be either caught or declared in the method signature, while unchecked exceptions do not require explicit handling.

ArrayList is backed by a dynamic array, providing fast random access but slower insertions and deletions. LinkedList is backed by a doubly-linked list, offering faster insertions and deletions but slower random access.

Autoboxing is the automatic conversion between primitive types and their corresponding wrapper classes. For example, converting an int to Integer.

The 'synchronized' keyword in Java is used to control access to a method or block of code by multiple threads, ensuring that only one thread can execute it at a time.

Multithreading in Java allows concurrent execution of two or more threads, enabling efficient CPU utilization and improved application performance.

A HashMap is a collection class that implements the Map interface, storing key-value pairs. It allows null values and keys and provides constant-time performance for basic operations.

Java achieves platform independence by compiling source code into bytecode, which is executed by the JVM. This allows Java programs to run on any platform that has a compatible JVM.

The Serializable interface provides a default mechanism for serialization, while the Externalizable interface allows for custom serialization behavior.

The 'volatile' keyword in Java indicates that a variable's value will be modified by multiple threads, ensuring that the most up-to-date value is always visible.

Serialization is the process of converting an object into a byte stream, enabling it to be saved to a file or transmitted over a network.

The finalize() method is called by the garbage collector before an object is destroyed, allowing for cleanup operations.

The 'final' keyword in Java is used to define constants, prevent method overriding, and prevent inheritance of classes, ensuring that certain elements remain unchanged.

Garbage collection is the process by which the JVM automatically deletes objects that are no longer reachable, freeing up memory resources.

'throw' is used to explicitly throw an exception, while 'throws' is used in method declarations to specify that a method can throw one or more exceptions.

The 'super' keyword in Java refers to the immediate parent class and is used to access parent class methods, constructors, and variables.

The JVM is responsible for loading, verifying, and executing Java bytecode. It provides an abstraction between the compiled Java program and the underlying hardware, enabling platform independence.

line

Copyrights © 2024 letsupdateskills All rights reserved