Table of Contents
- Introduction
- TreeSet vs Other Sets
- Constructors and Creation
- Natural Ordering
- Custom Comparators
- Basic Operations
- Navigation Methods
- Range Operations
- Performance Characteristics
- Use Cases
- Best Practices
- Complete Examples
Introduction
TreeSet is a NavigableSet implementation based on a TreeMap. It stores elements in a sorted order and provides guaranteed log(n) time cost for basic operations.
Key Characteristics:
- Sorted Order: Elements are stored in sorted order
- No Duplicates: Like all Set implementations
- Red-Black Tree: Backed by a balanced tree structure
- Null Elements: Doesn't allow null elements (if natural ordering)
- Performance: O(log n) for add, remove, contains operations
TreeSet vs Other Sets
Comparison Table:
| Feature | TreeSet | HashSet | LinkedHashSet |
|---|---|---|---|
| Ordering | Sorted (natural/comparator) | No order | Insertion order |
| Implementation | Red-Black Tree | Hash table | Hash table + Linked list |
| Performance | O(log n) | O(1) | O(1) |
| Null elements | Not allowed (if natural) | Allowed | Allowed |
| Use Case | Sorted data | General purpose | Insertion order |
Code Comparison:
import java.util.*;
public class SetComparison {
public static void main(String[] args) {
// TreeSet - Sorted order
Set<String> treeSet = new TreeSet<>();
treeSet.add("Orange");
treeSet.add("Apple");
treeSet.add("Banana");
treeSet.add("Cherry");
System.out.println("TreeSet: " + treeSet); // Sorted order
// HashSet - No order
Set<String> hashSet = new HashSet<>();
hashSet.add("Orange");
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Cherry");
System.out.println("HashSet: " + hashSet); // Random order
// LinkedHashSet - Insertion order
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Orange");
linkedHashSet.add("Apple");
linkedHashSet.add("Banana");
linkedHashSet.add("Cherry");
System.out.println("LinkedHashSet: " + linkedHashSet); // Insertion order
}
}
Output:
TreeSet: [Apple, Banana, Cherry, Orange] # Sorted order HashSet: [Apple, Cherry, Banana, Orange] # Random order LinkedHashSet: [Orange, Apple, Banana, Cherry] # Insertion order
Constructors and Creation
Available Constructors:
import java.util.*;
public class TreeSetConstructors {
public static void main(String[] args) {
// 1. Default constructor - natural ordering
TreeSet<String> defaultSet = new TreeSet<>();
defaultSet.add("Banana");
defaultSet.add("Apple");
defaultSet.add("Cherry");
System.out.println("Default: " + defaultSet);
// 2. Constructor with Comparator
TreeSet<String> reverseSet = new TreeSet<>(Comparator.reverseOrder());
reverseSet.add("Banana");
reverseSet.add("Apple");
reverseSet.add("Cherry");
System.out.println("Reverse: " + reverseSet);
// 3. Constructor from existing collection
List<String> list = Arrays.asList("Orange", "Grape", "Mango");
TreeSet<String> fromCollection = new TreeSet<>(list);
System.out.println("From collection: " + fromCollection);
// 4. Constructor from sorted set
TreeSet<String> fromSortedSet = new TreeSet<>(defaultSet);
System.out.println("From sorted set: " + fromSortedSet);
}
}
Output:
Default: [Apple, Banana, Cherry] Reverse: [Cherry, Banana, Apple] From collection: [Grape, Mango, Orange] From sorted set: [Apple, Banana, Cherry]
Natural Ordering
With Comparable Objects:
import java.util.*;
class Student implements Comparable<Student> {
private int id;
private String name;
private double gpa;
public Student(int id, String name, double gpa) {
this.id = id;
this.name = name;
this.gpa = gpa;
}
// Natural ordering by ID
@Override
public int compareTo(Student other) {
return Integer.compare(this.id, other.id);
}
@Override
public String toString() {
return String.format("Student{id=%d, name='%s', gpa=%.2f}", id, name, gpa);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return id == student.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
public class NaturalOrderingExample {
public static void main(String[] args) {
TreeSet<Student> students = new TreeSet<>();
students.add(new Student(103, "Alice", 3.8));
students.add(new Student(101, "Bob", 3.5));
students.add(new Student(102, "Charlie", 3.9));
students.add(new Student(100, "Diana", 3.7));
System.out.println("Students sorted by ID (natural order):");
for (Student student : students) {
System.out.println(student);
}
}
}
Output:
Students sorted by ID (natural order):
Student{id=100, name='Diana', gpa=3.70}
Student{id=101, name='Bob', gpa=3.50}
Student{id=102, name='Charlie', gpa=3.90}
Student{id=103, name='Alice', gpa=3.80}
Custom Comparators
Using Different Comparison Strategies:
import java.util.*;
class Product {
private String name;
private double price;
private int quantity;
public Product(String name, double price, int quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
public String getName() { return name; }
public double getPrice() { return price; }
public int getQuantity() { return quantity; }
@Override
public String toString() {
return String.format("Product{name='%-10s', price=$%6.2f, quantity=%2d}",
name, price, quantity);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Product)) return false;
Product product = (Product) o;
return Objects.equals(name, product.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
public class CustomComparatorExample {
public static void main(String[] args) {
// 1. Sort by name
TreeSet<Product> byName = new TreeSet<>(
Comparator.comparing(Product::getName)
);
// 2. Sort by price
TreeSet<Product> byPrice = new TreeSet<>(
Comparator.comparingDouble(Product::getPrice)
);
// 3. Sort by quantity (descending)
TreeSet<Product> byQuantityDesc = new TreeSet<>(
Comparator.comparingInt(Product::getQuantity).reversed()
);
// 4. Sort by price then by name
TreeSet<Product> byPriceThenName = new TreeSet<>(
Comparator.comparingDouble(Product::getPrice)
.thenComparing(Product::getName)
);
// Add products
List<Product> products = Arrays.asList(
new Product("Laptop", 999.99, 5),
new Product("Mouse", 25.99, 20),
new Product("Keyboard", 75.50, 15),
new Product("Monitor", 299.99, 8),
new Product("Mouse", 19.99, 25) // Different mouse
);
// Add to all sets
for (Product product : products) {
byName.add(product);
byPrice.add(product);
byQuantityDesc.add(product);
byPriceThenName.add(product);
}
System.out.println("Sorted by Name:");
byName.forEach(System.out::println);
System.out.println("\nSorted by Price:");
byPrice.forEach(System.out::println);
System.out.println("\nSorted by Quantity (Descending):");
byQuantityDesc.forEach(System.out::println);
System.out.println("\nSorted by Price then Name:");
byPriceThenName.forEach(System.out::println);
}
}
Output:
Sorted by Name:
Product{name='Keyboard ', price=$ 75.50, quantity=15}
Product{name='Laptop ', price=$999.99, quantity= 5}
Product{name='Monitor ', price=$299.99, quantity= 8}
Product{name='Mouse ', price=$ 25.99, quantity=20}
Sorted by Price:
Product{name='Mouse ', price=$ 25.99, quantity=20}
Product{name='Keyboard ', price=$ 75.50, quantity=15}
Product{name='Monitor ', price=$299.99, quantity= 8}
Product{name='Laptop ', price=$999.99, quantity= 5}
Sorted by Quantity (Descending):
Product{name='Mouse ', price=$ 25.99, quantity=20}
Product{name='Keyboard ', price=$ 75.50, quantity=15}
Product{name='Monitor ', price=$299.99, quantity= 8}
Product{name='Laptop ', price=$999.99, quantity= 5}
Sorted by Price then Name:
Product{name='Mouse ', price=$ 25.99, quantity=20}
Product{name='Keyboard ', price=$ 75.50, quantity=15}
Product{name='Monitor ', price=$299.99, quantity= 8}
Product{name='Laptop ', price=$999.99, quantity= 5}
Basic Operations
Common Operations Examples:
import java.util.*;
public class BasicOperations {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
// Add elements
numbers.add(50);
numbers.add(20);
numbers.add(80);
numbers.add(10);
numbers.add(30);
numbers.add(70);
numbers.add(40);
numbers.add(60);
System.out.println("TreeSet: " + numbers);
System.out.println("Size: " + numbers.size());
System.out.println("Contains 30: " + numbers.contains(30));
System.out.println("Contains 35: " + numbers.contains(35));
// Remove elements
numbers.remove(20);
System.out.println("After removing 20: " + numbers);
// First and last
System.out.println("First element: " + numbers.first());
System.out.println("Last element: " + numbers.last());
// Iteration (sorted order)
System.out.print("Iteration: ");
for (Integer num : numbers) {
System.out.print(num + " ");
}
System.out.println();
// Descending iteration
System.out.print("Descending iteration: ");
for (Integer num : numbers.descendingSet()) {
System.out.print(num + " ");
}
System.out.println();
// Clear
numbers.clear();
System.out.println("After clear - Empty: " + numbers.isEmpty());
System.out.println("Size: " + numbers.size());
}
}
Output:
TreeSet: [10, 20, 30, 40, 50, 60, 70, 80] Size: 8 Contains 30: true Contains 35: false After removing 20: [10, 30, 40, 50, 60, 70, 80] First element: 10 Last element: 80 Iteration: 10 30 40 50 60 70 80 Descending iteration: 80 70 60 50 40 30 10 After clear - Empty: true Size: 0
Navigation Methods
TreeSet Navigation Features:
import java.util.*;
public class NavigationMethods {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>(Arrays.asList(
10, 20, 30, 40, 50, 60, 70, 80, 90, 100
));
System.out.println("Original set: " + numbers);
// Lower and higher
System.out.println("Lower than 45: " + numbers.lower(45)); // Greatest element < 45
System.out.println("Higher than 45: " + numbers.higher(45)); // Smallest element > 45
// Floor and ceiling
System.out.println("Floor of 45: " + numbers.floor(45)); // Greatest element <= 45
System.out.println("Ceiling of 45: " + numbers.ceiling(45)); // Smallest element >= 45
// Head and tail sets
System.out.println("Head set (elements < 50): " + numbers.headSet(50));
System.out.println("Head set inclusive (elements <= 50): " + numbers.headSet(50, true));
System.out.println("Tail set (elements >= 60): " + numbers.tailSet(60));
System.out.println("Tail set exclusive (elements > 60): " + numbers.tailSet(60, false));
// Subset
System.out.println("Subset [40, 80): " + numbers.subSet(40, 80));
System.out.println("Subset [40, 80]: " + numbers.subSet(40, true, 80, true));
// Poll first and last
System.out.println("Poll first: " + numbers.pollFirst() + ", Remaining: " + numbers);
System.out.println("Poll last: " + numbers.pollLast() + ", Remaining: " + numbers);
}
}
Output:
Original set: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] Lower than 45: 40 Higher than 45: 50 Floor of 45: 40 Ceiling of 45: 50 Head set (elements < 50): [10, 20, 30, 40] Head set inclusive (elements <= 50): [10, 20, 30, 40, 50] Tail set (elements >= 60): [60, 70, 80, 90, 100] Tail set exclusive (elements > 60): [70, 80, 90, 100] Subset [40, 80): [40, 50, 60, 70] Subset [40, 80]: [40, 50, 60, 70, 80] Poll first: 10, Remaining: [20, 30, 40, 50, 60, 70, 80, 90, 100] Poll last: 100, Remaining: [20, 30, 40, 50, 60, 70, 80, 90]
Range Operations
Practical Range Query Examples:
import java.util.*;
public class RangeOperations {
public static void main(String[] args) {
TreeSet<Integer> scores = new TreeSet<>(Arrays.asList(
45, 67, 89, 92, 78, 56, 83, 71, 95, 62, 88, 76
));
System.out.println("All scores: " + scores);
// Find scores in different ranges
System.out.println("\n--- Grade Ranges ---");
System.out.println("Failing (< 60): " + scores.headSet(60));
System.out.println("Passing (60-69): " + scores.subSet(60, 70));
System.out.println("Good (70-79): " + scores.subSet(70, 80));
System.out.println("Very Good (80-89): " + scores.subSet(80, 90));
System.out.println("Excellent (>= 90): " + scores.tailSet(90));
// Find nearest scores
int target = 75;
System.out.println("\n--- Nearest to " + target + " ---");
System.out.println("Floor: " + scores.floor(target));
System.out.println("Ceiling: " + scores.ceiling(target));
System.out.println("Lower: " + scores.lower(target));
System.out.println("Higher: " + scores.higher(target));
// Range for scholarship (top 30%)
int minScholarshipScore = getPercentile(scores, 70);
System.out.println("\nScholarship candidates (top 30%): " +
scores.tailSet(minScholarshipScore));
}
private static int getPercentile(TreeSet<Integer> set, int percentile) {
if (set.isEmpty()) return 0;
int index = (int) Math.ceil(set.size() * percentile / 100.0) - 1;
return set.stream().sorted().skip(index).findFirst().orElse(0);
}
}
Output:
All scores: [45, 56, 62, 67, 71, 76, 78, 83, 88, 89, 92, 95] --- Grade Ranges --- Failing (< 60): [45, 56] Passing (60-69): [62, 67] Good (70-79): [71, 76, 78] Very Good (80-89): [83, 88, 89] Excellent (>= 90): [92, 95] --- Nearest to 75 --- Floor: 71 Ceiling: 76 Lower: 71 Higher: 76 Scholarship candidates (top 30%): [83, 88, 89, 92, 95]
Performance Characteristics
Time Complexity:
| Operation | Time Complexity |
|---|---|
| add() | O(log n) |
| remove() | O(log n) |
| contains() | O(log n) |
| first() / last() | O(1) |
| lower() / higher() | O(log n) |
| headSet() / tailSet() | O(log n) |
Performance Comparison:
import java.util.*;
public class PerformanceComparison {
public static void main(String[] args) {
final int SIZE = 100000;
// TreeSet
TreeSet<Integer> treeSet = new TreeSet<>();
long startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++) {
treeSet.add(i);
}
long treeSetAddTime = System.nanoTime() - startTime;
startTime = System.nanoTime();
treeSet.contains(SIZE / 2);
long treeSetContainsTime = System.nanoTime() - startTime;
// HashSet
HashSet<Integer> hashSet = new HashSet<>();
startTime = System.nanoTime();
for (int i = 0; i < SIZE; i++) {
hashSet.add(i);
}
long hashSetAddTime = System.nanoTime() - startTime;
startTime = System.nanoTime();
hashSet.contains(SIZE / 2);
long hashSetContainsTime = System.nanoTime() - startTime;
System.out.println("Performance Comparison (nanoseconds):");
System.out.printf("TreeSet - Add: %,d, Contains: %,d\n", treeSetAddTime, treeSetContainsTime);
System.out.printf("HashSet - Add: %,d, Contains: %,d\n", hashSetAddTime, hashSetContainsTime);
System.out.printf("TreeSet add is %.2fx slower than HashSet\n",
(double) treeSetAddTime / hashSetAddTime);
}
}
Use Cases
1. Sorted Leaderboard:
import java.util.*;
class Player implements Comparable<Player> {
private String name;
private int score;
public Player(String name, int score) {
this.name = name;
this.score = score;
}
// Higher scores first
@Override
public int compareTo(Player other) {
return Integer.compare(other.score, this.score); // Descending
}
public String getName() { return name; }
public int getScore() { return score; }
@Override
public String toString() {
return String.format("%-10s: %d", name, score);
}
}
public class Leaderboard {
private TreeSet<Player> players;
public Leaderboard() {
players = new TreeSet<>();
}
public void addPlayer(String name, int score) {
players.add(new Player(name, score));
}
public void displayTopPlayers(int count) {
System.out.println("Top " + count + " Players:");
int rank = 1;
for (Player player : players) {
if (rank > count) break;
System.out.println(rank + ". " + player);
rank++;
}
}
public void displayRank(String playerName) {
int rank = 1;
for (Player player : players) {
if (player.getName().equals(playerName)) {
System.out.println(playerName + " is ranked #" + rank);
return;
}
rank++;
}
System.out.println(playerName + " not found in leaderboard");
}
public static void main(String[] args) {
Leaderboard leaderboard = new Leaderboard();
leaderboard.addPlayer("Alice", 1500);
leaderboard.addPlayer("Bob", 1800);
leaderboard.addPlayer("Charlie", 1200);
leaderboard.addPlayer("Diana", 2000);
leaderboard.addPlayer("Eve", 1600);
leaderboard.addPlayer("Frank", 1900);
leaderboard.displayTopPlayers(3);
System.out.println();
leaderboard.displayRank("Alice");
leaderboard.displayRank("Diana");
}
}
Output:
Top 3 Players: 1. Diana : 2000 2. Frank : 1900 3. Bob : 1800 Alice is ranked #5 Diana is ranked #1
2. Event Scheduler:
import java.util.*;
class Event implements Comparable<Event> {
private String name;
private Date timestamp;
public Event(String name, Date timestamp) {
this.name = name;
this.timestamp = timestamp;
}
@Override
public int compareTo(Event other) {
return this.timestamp.compareTo(other.timestamp);
}
public String getName() { return name; }
public Date getTimestamp() { return timestamp; }
@Override
public String toString() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return String.format("Event{name='%s', time=%s}", name, sdf.format(timestamp));
}
}
public class EventScheduler {
private TreeSet<Event> events;
public EventScheduler() {
events = new TreeSet<>();
}
public void scheduleEvent(String name, Date time) {
events.add(new Event(name, time));
System.out.println("Scheduled: " + name + " at " + time);
}
public Event getNextEvent() {
return events.isEmpty() ? null : events.first();
}
public Event getNextEventAfter(Date time) {
Event next = events.higher(new Event("", time));
return next;
}
public Set<Event> getEventsBetween(Date start, Date end) {
return events.subSet(new Event("", start), true, new Event("", end), true);
}
public void displayUpcomingEvents(int count) {
System.out.println("\nUpcoming " + count + " events:");
int displayed = 0;
for (Event event : events) {
if (displayed >= count) break;
System.out.println((displayed + 1) + ". " + event);
displayed++;
}
}
public static void main(String[] args) throws Exception {
EventScheduler scheduler = new EventScheduler();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
// Schedule events
scheduler.scheduleEvent("Meeting", sdf.parse("2024-01-15 10:00"));
scheduler.scheduleEvent("Lunch", sdf.parse("2024-01-15 12:30"));
scheduler.scheduleEvent("Conference", sdf.parse("2024-01-15 09:00"));
scheduler.scheduleEvent("Training", sdf.parse("2024-01-15 14:00"));
scheduler.displayUpcomingEvents(3);
Date searchTime = sdf.parse("2024-01-15 11:00");
Event next = scheduler.getNextEventAfter(searchTime);
System.out.println("\nNext event after " + sdf.format(searchTime) + ": " + next);
}
}
Output:
Scheduled: Meeting at Mon Jan 15 10:00:00 IST 2024
Scheduled: Lunch at Mon Jan 15 12:30:00 IST 2024
Scheduled: Conference at Mon Jan 15 09:00:00 IST 2024
Scheduled: Training at Mon Jan 15 14:00:00 IST 2024
Upcoming 3 events:
1. Event{name='Conference', time=2024-01-15 09:00:00}
2. Event{name='Meeting', time=2024-01-15 10:00:00}
3. Event{name='Lunch', time=2024-01-15 12:30:00}
Next event after 2024-01-15 11:00: Lunch
Best Practices
1. Consistent equals() and compareTo():
class ConsistentStudent implements Comparable<ConsistentStudent> {
private int id;
private String name;
public ConsistentStudent(int id, String name) {
this.id = id;
this.name = name;
}
// compareTo consistent with equals
@Override
public int compareTo(ConsistentStudent other) {
return Integer.compare(this.id, other.id);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ConsistentStudent)) return false;
ConsistentStudent that = (ConsistentStudent) o;
return id == that.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
2. Handle Null Values:
public class NullHandling {
public static void main(String[] args) {
// TreeSet with natural ordering doesn't allow null
try {
TreeSet<String> naturalSet = new TreeSet<>();
naturalSet.add(null); // Throws NullPointerException
} catch (NullPointerException e) {
System.out.println("Natural ordering TreeSet doesn't allow null");
}
// TreeSet with custom comparator can allow null
TreeSet<String> customSet = new TreeSet<>(
Comparator.nullsFirst(Comparator.naturalOrder())
);
customSet.add(null);
customSet.add("Apple");
customSet.add("Banana");
System.out.println("With nullsFirst: " + customSet);
}
}
3. Choose Appropriate Initial Capacity:
public class TreeSetConfiguration {
public static void main(String[] args) {
// For large datasets, consider initial capacity
// (Though TreeSet doesn't have capacity constructor like HashSet)
// Better approach: Use appropriate comparator
TreeSet<Integer> largeSet = new TreeSet<>(Comparator.reverseOrder());
// Add bulk data efficiently
List<Integer> bulkData = Arrays.asList(5, 2, 8, 1, 9, 3, 7, 4, 6);
largeSet.addAll(bulkData);
System.out.println("Large set: " + largeSet);
}
}
Complete Examples
Complete Working Example:
import java.util.*;
import java.text.SimpleDateFormat;
public class TreeSetCompleteExample {
public static void main(String[] args) throws Exception {
System.out.println("=== TreeSet Complete Example ===\n");
// Example 1: Basic Sorted Operations
basicSortedOperations();
// Example 2: Custom Object Sorting
customObjectSorting();
// Example 3: Navigation and Range Queries
navigationAndRangeQueries();
// Example 4: Real-world Use Case - Product Catalog
productCatalogExample();
}
public static void basicSortedOperations() {
System.out.println("1. Basic Sorted Operations:");
TreeSet<String> fruits = new TreeSet<>();
// Add in random order
fruits.add("Orange");
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
fruits.add("Mango");
fruits.add("Grape");
System.out.println("Natural order: " + fruits);
System.out.println("Reverse order: " + fruits.descendingSet());
System.out.println("First fruit: " + fruits.first());
System.out.println("Last fruit: " + fruits.last());
System.out.println();
}
public static void customObjectSorting() {
System.out.println("2. Custom Object Sorting:");
// Sort by name
TreeSet<Employee> byName = new TreeSet<>(
Comparator.comparing(Employee::getName)
);
// Sort by salary (descending)
TreeSet<Employee> bySalary = new TreeSet<>(
Comparator.comparingDouble(Employee::getSalary).reversed()
);
// Add employees
byName.add(new Employee("John", 50000, "IT"));
byName.add(new Employee("Alice", 60000, "HR"));
byName.add(new Employee("Bob", 55000, "Finance"));
byName.add(new Employee("Diana", 75000, "IT"));
bySalary.addAll(byName);
System.out.println("Sorted by Name:");
byName.forEach(System.out::println);
System.out.println("\nSorted by Salary (Descending):");
bySalary.forEach(System.out::println);
System.out.println();
}
public static void navigationAndRangeQueries() {
System.out.println("3. Navigation and Range Queries:");
TreeSet<Integer> numbers = new TreeSet<>(Arrays.asList(
10, 25, 35, 40, 50, 65, 70, 85, 90, 100
));
System.out.println("All numbers: " + numbers);
int target = 45;
System.out.println("\nNavigation around " + target + ":");
System.out.println("Floor: " + numbers.floor(target));
System.out.println("Ceiling: " + numbers.ceiling(target));
System.out.println("Lower: " + numbers.lower(target));
System.out.println("Higher: " + numbers.higher(target));
System.out.println("\nRange queries:");
System.out.println("Numbers < 50: " + numbers.headSet(50));
System.out.println("Numbers >= 70: " + numbers.tailSet(70));
System.out.println("Numbers between 30 and 80: " + numbers.subSet(30, 80));
System.out.println();
}
public static void productCatalogExample() throws Exception {
System.out.println("4. Product Catalog Example:");
ProductCatalog catalog = new ProductCatalog();
// Add products
catalog.addProduct(new CatalogProduct("Laptop", 999.99, "Electronics"));
catalog.addProduct(new CatalogProduct("Mouse", 25.99, "Electronics"));
catalog.addProduct(new CatalogProduct("Desk", 199.99, "Furniture"));
catalog.addProduct(new CatalogProduct("Chair", 149.99, "Furniture"));
catalog.addProduct(new CatalogProduct("Monitor", 299.99, "Electronics"));
System.out.println("All products by name:");
catalog.displayAllProducts();
System.out.println("\nProducts under $200:");
catalog.displayProductsUnderPrice(200);
System.out.println("\nProducts in price range $100-$300:");
catalog.displayProductsInPriceRange(100, 300);
System.out.println("\nElectronics products:");
catalog.displayProductsByCategory("Electronics");
}
}
// Supporting classes
class Employee {
private String name;
private double salary;
private String department;
public Employee(String name, double salary, String department) {
this.name = name;
this.salary = salary;
this.department = department;
}
public String getName() { return name; }
public double getSalary() { return salary; }
public String getDepartment() { return department; }
@Override
public String toString() {
return String.format("Employee{name='%-6s', salary=$%6.2f, department='%s'}",
name, salary, department);
}
}
class CatalogProduct implements Comparable<CatalogProduct> {
private String name;
private double price;
private String category;
public CatalogProduct(String name, double price, String category) {
this.name = name;
this.price = price;
this.category = category;
}
@Override
public int compareTo(CatalogProduct other) {
return this.name.compareTo(other.name);
}
public String getName() { return name; }
public double getPrice() { return price; }
public String getCategory() { return category; }
@Override
public String toString() {
return String.format("Product{name='%-10s', price=$%6.2f, category='%s'}",
name, price, category);
}
}
class ProductCatalog {
private TreeSet<CatalogProduct> productsByName;
private TreeSet<CatalogProduct> productsByPrice;
public ProductCatalog() {
productsByName = new TreeSet<>();
productsByPrice = new TreeSet<>(
Comparator.comparingDouble(CatalogProduct::getPrice)
);
}
public void addProduct(CatalogProduct product) {
productsByName.add(product);
productsByPrice.add(product);
}
public void displayAllProducts() {
productsByName.forEach(System.out::println);
}
public void displayProductsUnderPrice(double maxPrice) {
CatalogProduct dummy = new CatalogProduct("", maxPrice, "");
productsByPrice.headSet(dummy, true).forEach(System.out::println);
}
public void displayProductsInPriceRange(double minPrice, double maxPrice) {
CatalogProduct minDummy = new CatalogProduct("", minPrice, "");
CatalogProduct maxDummy = new CatalogProduct("", maxPrice, "");
productsByPrice.subSet(minDummy, true, maxDummy, true)
.forEach(System.out::println);
}
public void displayProductsByCategory(String category) {
productsByName.stream()
.filter(p -> p.getCategory().equals(category))
.forEach(System.out::println);
}
}
Expected Output:
=== TreeSet Complete Example ===
1. Basic Sorted Operations:
Natural order: [Apple, Banana, Cherry, Grape, Mango, Orange]
Reverse order: [Orange, Mango, Grape, Cherry, Banana, Apple]
First fruit: Apple
Last fruit: Orange
2. Custom Object Sorting:
Sorted by Name:
Employee{name='Alice ', salary=$60000.00, department='HR'}
Employee{name='Bob ', salary=$55000.00, department='Finance'}
Employee{name='Diana ', salary=$75000.00, department='IT'}
Employee{name='John ', salary=$50000.00, department='IT'}
Sorted by Salary (Descending):
Employee{name='Diana ', salary=$75000.00, department='IT'}
Employee{name='Alice ', salary=$60000.00, department='HR'}
Employee{name='Bob ', salary=$55000.00, department='Finance'}
Employee{name='John ', salary=$50000.00, department='IT'}
3. Navigation and Range Queries:
All numbers: [10, 25, 35, 40, 50, 65, 70, 85, 90, 100]
Navigation around 45:
Floor: 40
Ceiling: 50
Lower: 40
Higher: 50
Range queries:
Numbers < 50: [10, 25, 35, 40]
Numbers >= 70: [70, 85, 90, 100]
Numbers between 30 and 80: [35, 40, 50, 65, 70]
4. Product Catalog Example:
All products by name:
Product{name='Chair ', price=$149.99, category='Furniture'}
Product{name='Desk ', price=$199.99, category='Furniture'}
Product{name='Laptop ', price=$999.99, category='Electronics'}
Product{name='Monitor ', price=$299.99, category='Electronics'}
Product{name='Mouse ', price=$ 25.99, category='Electronics'}
Products under $200:
Product{name='Mouse ', price=$ 25.99, category='Electronics'}
Product{name='Chair ', price=$149.99, category='Furniture'}
Product{name='Desk ', price=$199.99, category='Furniture'}
Products in price range $100-$300:
Product{name='Chair ', price=$149.99, category='Furniture'}
Product{name='Desk ', price=$199.99, category='Furniture'}
Product{name='Monitor ', price=$299.99, category='Electronics'}
Electronics products:
Product{name='Laptop ', price=$999.99, category='Electronics'}
Product{name='Monitor ', price=$299.99, category='Electronics'}
Product{name='Mouse ', price=$ 25.99, category='Electronics'}
Key Takeaways
- TreeSet maintains sorted order - elements are always sorted
- No duplicates - standard Set behavior
- Red-Black Tree implementation - provides O(log n) performance
- Natural ordering or custom comparator - flexible sorting
- Rich navigation API - ceiling, floor, higher, lower methods
- Range operations - headSet, tailSet, subSet for efficient queries
- No null elements - with natural ordering (unless specified in comparator)
- Perfect for sorted data - leaderboards, event schedulers, sorted catalogs
TreeSet is the ideal choice when you need a sorted collection with efficient range queries and navigation operations, making it perfect for applications that require maintaining data in sorted order.