The TreeSet class in Java is part of the Java Collections Framework and is located in the java.util package. It implements the NavigableSet interface and is backed by a Red-Black Tree.

Key Points

Following are a few key points to note about TreeSet in Java:

  • TreeSet cannot contain duplicate elements.
  • The elements in a TreeSet are sorted as per their natural ordering, or based on a custom Comparator that is supplied at the time of creation of the TreeSet.
  • TreeSet cannot contain a null value.
  • TreeSet internally uses a TreeMap to store elements.
  • TreeSet class is not thread-safe. You must explicitly synchronize concurrent access to a TreeSet in a multi-threaded environment.

Creating a TreeSet and Adding New Elements to It

import java.util.TreeSet;

public class TreeSetExample {

    public static void main(String[] args) {

        // 1. Create a new TreeSet
        TreeSet<String> treeSet = new TreeSet<>();

        // 2. Add elements to the TreeSet
        treeSet.add("Apple");
        treeSet.add("Banana");
        treeSet.add("Cherry");

        // 3. Print the elements of the TreeSet
        for (String fruit : treeSet) {
            System.out.println(fruit);
        }
    }
}

Output:

Apple
Banana
Cherry

Explanation:

1. A new TreeSet is created which is intended to hold String elements. Being a set, it won't allow duplicate values.

2. Three string elements representing different fruits are added to the TreeSet.

3. The contents of the TreeSet are then printed. Since TreeSet is a sorted set, the output will always be in ascending order, provided that the elements in the set are comparable.

TreeSet with a Custom Comparator (Descending Order)

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

public class DescendingTreeSetExample {

    public static void main(String[] args) {

        // 1. Create a comparator for descending order
        Comparator<String> descendingOrder = (s1, s2) -> s2.compareTo(s1);

        // 2. Create a new TreeSet with the custom comparator
        TreeSet<String> treeSet = new TreeSet<>(descendingOrder);

        // 3. Add elements to the TreeSet
        treeSet.add("Apple");
        treeSet.add("Banana");
        treeSet.add("Cherry");

        // 4. Print the elements of the TreeSet
        for (String fruit : treeSet) {
            System.out.println(fruit);
        }
    }
}

Output:

Cherry
Banana
Apple

Explanation:

1. A custom Comparator named descendingOrder is defined for String elements. This comparator reverses the natural ordering of strings, effectively causing them to be sorted in descending order.

2. A new TreeSet is created, taking the custom Comparator as an argument. This ensures that the elements added to the set will be ordered in descending order.

3. Three string elements representing different fruits are added to the TreeSet.

4. The contents of the TreeSet are then printed. Due to the custom Comparator, the output is in descending order of the elements.

Accessing the Elements of a TreeSet

import java.util.TreeSet;

public class TreeSetAccessExample {

    public static void main(String[] args) {

        // 1. Create a TreeSet and add elements
        TreeSet<String> treeSet = new TreeSet<>();
        treeSet.add("Apple");
        treeSet.add("Banana");
        treeSet.add("Cherry");
        treeSet.add("Date");
        treeSet.add("Elderberry");

        // 2. Find and print the size of the TreeSet
        System.out.println("Size of the TreeSet: " + treeSet.size());

        // 3. Check if an element exists in the TreeSet
        System.out.println("Does the TreeSet contain &apos;Banana&apos;? " + treeSet.contains("Banana"));

        // 4. Find and print the first element in the TreeSet
        System.out.println("First element: " + treeSet.first());

        // 5. Find and print the last element in the TreeSet
        System.out.println("Last element: " + treeSet.last());

        // 6. Find the element just higher than &apos;Cherry&apos;
        System.out.println("Element just higher than &apos;Cherry&apos;: " + treeSet.higher("Cherry"));

        // 7. Find the element just lower than &apos;Cherry&apos;
        System.out.println("Element just lower than &apos;Cherry&apos;: " + treeSet.lower("Cherry"));
    }
}

Output:

Size of the TreeSet: 5
Does the TreeSet contain 'Banana'? true
First element: Apple
Last element: Elderberry
Element just higher than 'Cherry': Date
Element just lower than 'Cherry': Banana

Explanation:

1. A new TreeSet is created and several elements representing different fruits are added to it. The TreeSet automatically maintains the order of the elements in ascending order.

2. The size() method is used to find out how many elements are in the TreeSet and the result is printed.

3. The contains() method is used to check if a particular element, in this case, "Banana", exists in the TreeSet.

4. The first() method returns the first (smallest) element in the TreeSet.

5. The last() method returns the last (largest) element in the TreeSet.

6. The higher() method returns the next element after the given element in the sorted TreeSet. In this case, it retrieves the element just after "Cherry".

7. The lower() method returns the element just before the given element in the sorted TreeSet. Here, it retrieves the element just before "Cherry".

Removing Elements from a TreeSet

import java.util.TreeSet;
import java.util.function.Predicate;

public class TreeSetRemoveExample {

    public static void main(String[] args) {

        // 1. Create a TreeSet and add elements
        TreeSet<String> treeSet = new TreeSet<>();
        treeSet.add("Apple");
        treeSet.add("Banana");
        treeSet.add("Cherry");
        treeSet.add("Date");
        treeSet.add("Elderberry");
        System.out.println("Initial TreeSet: " + treeSet);

        // 2. Remove an element from the TreeSet
        treeSet.remove("Date");
        System.out.println("After removing &apos;Date&apos;: " + treeSet);

        // 3. Remove all elements that satisfy a given predicate
        Predicate<String> condition = fruit -> fruit.startsWith("B"); // Remove all fruits starting with "B"
        treeSet.removeIf(condition);
        System.out.println("After removing fruits starting with &apos;B&apos;: " + treeSet);

        // 4. Remove the first element of the TreeSet
        treeSet.pollFirst();
        System.out.println("After removing the first element: " + treeSet);

        // 5. Remove the last element of the TreeSet
        treeSet.pollLast();
        System.out.println("After removing the last element: " + treeSet);
    }
}

Output:

After removing 'Date': [Apple, Banana, Cherry, Elderberry]
After removing fruits starting with 'B': [Apple, Cherry, Elderberry]
After removing the first element: [Cherry, Elderberry]
After removing the last element: [Cherry]

Explanation:

1. A new TreeSet is created and several elements representing different fruits are added to it. The TreeSet maintains the order of the elements in ascending order.

2. The remove() method is used to delete a specific element, "Date", from the TreeSet.

3. The removeIf() method is utilized to delete elements based on a given condition. Here, it removes all fruits that start with the letter "B".

4. The pollFirst() method deletes and returns the first (smallest) element in the TreeSet.

5. The pollLast() method deletes and returns the last (largest) element in the TreeSet.

Iterating over a TreeSet – Different Ways

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

public class TreeSetIteration {

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

        // 1. Iterate using Java 8 forEach and lambda expression
        System.out.println("Using Java 8 forEach with lambda:");
        treeSet.forEach(item -> System.out.println(item));

        // 2. Iterate over a TreeSet using iterator()
        System.out.println("\nUsing iterator():");
        Iterator<String> iterator = treeSet.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        // 3. Iterate over a TreeSet using iterator() and Java 8 forEachRemaining() method
        System.out.println("\nUsing iterator() with forEachRemaining():");
        iterator = treeSet.iterator();
        iterator.forEachRemaining(item -> System.out.println(item));

        // 4. Iterate over a TreeSet using simple for-each loop
        System.out.println("\nUsing for-each loop:");
        for (String item : treeSet) {
            System.out.println(item);
        }
    }
}

Output:

Using Java 8 forEach with lambda:
Apple
Banana
Cherry

Using iterator():
Apple
Banana
Cherry

Using iterator() with forEachRemaining():
Apple
Banana
Cherry

Using for-each loop:
Apple
Banana
Cherry

Explanation:

1. The forEach method from Java 8 is utilized along with a lambda expression to iterate through the TreeSet and print each element.

2. A traditional iterator() method is used, where we get an iterator from the TreeSet and use it in a while loop to go through each element.

3. The forEachRemaining() method, also introduced in Java 8, is used with an iterator to traverse and print each element. It's a more concise way of using an iterator with lambda expressions.

4. A simple enhanced for-each loop is used, which is a common way to iterate over collections in Java.

TreeSet with User-Defined Objects

import java.util.TreeSet;

class Student implements Comparable<Student> {
    int id;
    String name;

    // Constructor
    Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public int compareTo(Student o) {
        // Compare based on student IDs
        return this.id - o.id;
    }

    @Override
    public String toString() {
        return "ID: " + id + ", Name: " + name;
    }
}

public class TreeSetWithObjects {

    public static void main(String[] args) {
        // Create a TreeSet of custom Student objects
        TreeSet<Student> students = new TreeSet<>();

        // 1. Add new student objects to the TreeSet
        students.add(new Student(3, "Alice"));
        students.add(new Student(1, "Bob"));
        students.add(new Student(2, "Charlie"));

        // 2. Display the TreeSet
        System.out.println(students);
    }
}

Output:

[ID: 1, Name: Bob, ID: 2, Name: Charlie, ID: 3, Name: Alice]

Explanation:

1. A Student class is defined which implements the Comparable interface, making it possible for instances of this class to be added to a TreeSet. Inside the compareTo() method, we are comparing Student objects based on their id, which ensures that the students in the TreeSet are ordered by their IDs in ascending order.

2. We instantiate the TreeSet with custom Student objects and add three students.

3. When the TreeSet is displayed, the students are automatically sorted in ascending order of their IDs, as specified by the compareTo() method.

Conclusion

In this tutorial, 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 iterate over a TreeSet, and how to create a TreeSet of user-defined objects.