Java TreeSet

Java TreeSet is a class in Java that implements the SortedSet interface from the Java Collections Framework.

Important Key Points About Java TreeSet Class

Ordering: 

TreeSet stores elements in a sorted manner, either in natural order (for objects that implement Comparable) or by a Comparator provided at set creation. 

Uniqueness: 

TreeSet cannot contain duplicate elements.

Null Elements: 

TreeSet cannot contain a null value.

Performance: 

TreeSet operations like add, remove, and contains methods take O(log(n)) time. 

Not Synchronized: 

TreeSet is not synchronized, which means it is not suitable for threaded environments unless externally synchronized. 

Traversal: 

The iterator provided by TreeSet is fail-fast, which means any concurrent modification of the set while iterating over it will result in a ConcurrentModificationException. 

Implements NavigableSet: 

TreeSet implements the NavigableSet interface, providing methods for navigation (lower, floor, ceiling, higher), and for retrieval and removal of the first and last element. 

Storage:

TreeSet internally uses a TreeMap to store elements.

Create TreeSet 

Here is the syntax to create an object of TreeSet class:
// Creating a TreeSet
TreeSet<String> fruits = new TreeSet<>();

Add Elements

We can use add() method to add elements to TreeSet:
// Creating a TreeSet
TreeSet<String> fruits = new TreeSet<>();

// Adding new elements to a TreeSet
fruits.add("Banana");
fruits.add("Apple");
fruits.add("Pineapple");
fruits.add("Orange");

System.out.println("Fruits Set : " + fruits);

// Duplicate elements are ignored
fruits.add("Apple");
System.out.println("After adding duplicate element \"Apple\" : " + fruits);

// This will be allowed because it's in lowercase.
fruits.add("banana");
System.out.println("After adding \"banana\" : " + fruits);
Output:
Fruits Set : [Apple, Banana, Orange, Pineapple]
After adding duplicate element "Apple" : [Apple, Banana, Orange, Pineapple]
After adding "banana" : [Apple, Banana, Orange, Pineapple, banana]
Note that in this example, duplicate elements are ignored.

TreeSet with a custom comparator (Case Insensitive Order)

Let's create a TreeSet with a custom comparator that sorts the elements by ignoring the case.
// Creating a TreeSet with a custom Comparator (Case Insensitive Order)
SortedSet<String> weekDays = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
// Adding new elements to a TreeSet
weekDays.add("Monday");
weekDays.add("Tuesday");
weekDays.add("Wednesday");
weekDays.add("Thursday ");
weekDays.add("Friday ");
weekDays.add("Saturday  ");
weekDays.add("Sunday");

System.out.println("weekDays Set : " + weekDays);

// Now, lowercase elements will also be considered as duplicates
weekDays.add("sunday");
System.out.println("After adding \"sunday\" : " + weekDays);
Output:
weekDays Set : [Friday , Monday, Saturday  , Sunday, Thursday , Tuesday, Wednesday]
After adding "sunday" : [Friday , Monday, Saturday  , Sunday, Thursday , Tuesday, Wednesday]
Note that the 'Sunday' element is case Insensitive which is ignored.

TreeSet with a custom Comparator Example(Descending order)

// Creating a TreeSet with a custom Comparator (Descending  Order)
SortedSet<String> weekDays = new TreeSet<>(Comparator.reverseOrder());
// Adding new elements to a TreeSet
weekDays.add("Monday");
weekDays.add("Tuesday");
weekDays.add("Wednesday");
weekDays.add("Thursday ");
weekDays.add("Friday ");
weekDays.add("Saturday  ");
weekDays.add("Sunday");

System.out.println("weekDays Set : " + weekDays);
Output:
weekDays Set : [Wednesday, Tuesday, Thursday , Sunday, Saturday  , Monday, Friday ]

Accessing the elements of a TreeSet

Example 1: Find the size of a TreeSet.
TreeSet<String> students = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);

students.add("Ramesh");
students.add("Prakash");
students.add("Amir");
// Finding the size of a TreeSet
System.out.println("Number of elements in the TreeSet : " + students.size());
Example 2: Check if an element exists in a TreeSet.
TreeSet<String> students = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);

students.add("Ramesh");
students.add("Prakash");
students.add("Amir");

// Check if an element exists in the TreeSet
String name = "Julia";
if(students.contains(name)) {
    System.out.println("TreeSet contains the element : " + name);
} else {
    System.out.println("TreeSet does not contain the element : " + name);
}
Example 3: Find the first element in the TreeSet.
TreeSet<String> students = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);

students.add("Ramesh");
students.add("Prakash");
students.add("Amir");

// Navigating through the TreeSet
System.out.println("First element : " + students.first());
Example 4: Find the last element in the TreeSet.
TreeSet<String> students = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);

students.add("Ramesh");
students.add("Prakash");
students.add("Amir");
System.out.println("Last element : " + students.last());
Example 5: Find the element just higher than the given element in the TreeSet.
TreeSet<String> students = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);

students.add("Ramesh");
students.add("Prakash");
students.add("Amir");
String name = "amir";
System.out.println("Element just greater than "  + name + " : " + students.higher(name));
Example 6: Find the element just lower than the given element in the TreeSet.
TreeSet<String> students = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);

students.add("Ramesh");
students.add("Prakash");
students.add("Amir");
String name = "amir";
System.out.println("Element just lower than "  + name + " : " + students.lower(name));

Removing elements from a TreeSet

Example 1: Remove an element from a TreeSet.
TreeSet<Integer> numbers = new TreeSet<>();

numbers.add(10);
numbers.add(15);
numbers.add(20);
numbers.add(25);

// Remove an element from the TreeSet
boolean isRemoved = numbers.remove(49);
Example 2: Remove all elements that satisfy a given predicate.
TreeSet<Integer> numbers = new TreeSet<>();

numbers.add(10);
numbers.add(15);
numbers.add(20);
numbers.add(25);
// Remove all elements divisible by 3
numbers.removeIf(number -> number % 3 == 0);
Example 3: Remove the first element of the TreeSet.
TreeSet<Integer> numbers = new TreeSet<>();

numbers.add(10);
numbers.add(15);
numbers.add(20);
numbers.add(25);
// Retrieve and remove the first element from the TreeSet
Integer num = numbers.pollFirst();
System.out.println("Removed first element " + num + " from the TreeSet : " + numbers);
Example 4: Remove the last element of the TreeSet.
TreeSet<Integer> numbers = new TreeSet<>();

numbers.add(10);
numbers.add(15);
numbers.add(20);
numbers.add(25);
// Retrieve and remove the last element from the TreeSet
num = numbers.pollLast();
System.out.println("Removed last element " + num + " from the TreeSet : " + numbers);

Searching Elements in TreeSet

To search for elements in a TreeSet, we can use the contains() method. It returns true if the set contains the specified element and false otherwise. 

Here is an example with output:
import java.util.TreeSet;

public class Main {
    public static void main(String[] args) {
        TreeSet<String> treeSet = new TreeSet<>();
        treeSet.add("Orange");
        treeSet.add("Apple");
        treeSet.add("Banana");

        System.out.println("Does the TreeSet contain 'Apple'? " + treeSet.contains("Apple"));
        System.out.println("Does the TreeSet contain 'Grapes'? " + treeSet.contains("Grapes"));
    }
}

Output:

Does the TreeSet contain 'Apple'? true
Does the TreeSet contain 'Grapes'? false
In this example, "Apple" is in the TreeSet, so treeSet.contains("Apple") returns true. "Grapes" is not in the TreeSet, so treeSet.contains("Grapes") returns false.

Iterating Over TreeSet

There are different ways to iterate over TreeSet:
  • Iterating with Iterator
  • Iterating with Enhanced for-loop
  • Iterating with Java 8 forEach and lambda
  • Iterating with Java 8 Stream API
Here is the complete program to demonstrate different ways to iterate over TreeSet:
import java.util.Iterator;
import java.util.TreeSet;

public class Main {
    public static void main(String[] args) {
        TreeSet<String> treeSet = new TreeSet<>();
        treeSet.add("Orange");
        treeSet.add("Apple");
        treeSet.add("Banana");

        // 1. Using Iterator
        System.out.println("Iterating with Iterator:");
        Iterator<String> iterator = treeSet.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

        // 2. Using Enhanced for-loop
        System.out.println("\nIterating with Enhanced for-loop:");
        for (String fruit : treeSet) {
            System.out.println(fruit);
        }

        // 3. Using Java 8 forEach with lambda
        System.out.println("\nIterating with Java 8 forEach and lambda:");
        treeSet.forEach(fruit -> System.out.println(fruit));

        // 4. Using Java 8 Stream API
        System.out.println("\nIterating with Java 8 Stream API:");
        treeSet.stream().forEach(System.out::println);
    }
}

Output:

Iterating with Iterator:
Apple
Banana
Orange

Iterating with Enhanced for-loop:
Apple
Banana
Orange

Iterating with Java 8 forEach and lambda:
Apple
Banana
Orange

Iterating with Java 8 Stream API:
Apple
Banana
Orange

TreeSet with User-Defined Objects

Using a TreeSet with user-defined objects requires that those objects implement the Comparable interface, or you provide a custom Comparator at the time of creation of the TreeSet. This is needed because a TreeSet is a sorted collection, and Java needs to know by what criteria the objects should be sorted. 

Here is an example of using a TreeSet with a user-defined class Person:
import java.util.TreeSet;

class Person implements Comparable<Person> {
    String name;
    int age;

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

    @Override
    public int compareTo(Person other) {
        return this.name.compareTo(other.name); // comparing based on name
    }

    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

public class Main {
    public static void main(String[] args) {
        TreeSet<Person> people = new TreeSet<>();
        people.add(new Person("John", 25));
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 20));

        for (Person person : people) {
            System.out.println(person);
        }
    }
}

Output:

Person{name='Alice', age=30}
Person{name='Bob', age=20}
Person{name='John', age=25}
In this example, the Person class implements Comparable<Person>. This means that we're defining a natural order for persons based on their names. When we add Person objects to the TreeSet, they're automatically sorted by their names.

Conclusion

In this article, you learned what is a TreeSet in Java, how to create a TreeSet, how to pass a custom comparator to the TreeSet to alter the sorting order of the elements, how to access the elements of a TreeSet, how to remove elements from a TreeSet, how to search elements in a TreeSet, how to iterate over a TreeSet, and how to create a TreeSet of user-defined objects.

Comments