Java Collections synchronizedNavigableSet() Method

The synchronizedNavigableSet() method in Java is a utility method provided by the java.util.Collections class. It returns a synchronized (thread-safe) navigable set backed by the specified navigable set. This method is particularly useful when you need to ensure that a navigable set is thread-safe in a concurrent environment.

Table of Contents

  1. Introduction
  2. synchronizedNavigableSet() Method Syntax
  3. Examples
    • Basic Usage of synchronizedNavigableSet()
    • Using synchronizedNavigableSet() with Custom Classes
  4. Real-World Use Case
  5. Conclusion

Introduction

The Collections.synchronizedNavigableSet() method provides a way to wrap a given navigable set with synchronized access, ensuring that only one thread can access the set at a time. This is crucial in concurrent applications where multiple threads might try to modify the set simultaneously, leading to race conditions or inconsistent data.

The returned navigable set is a synchronized view of the specified set, meaning that all operations on the set are synchronized using the set’s intrinsic lock. This includes operations such as adding, removing, or iterating over the elements, as well as any navigable-specific operations like lower, floor, ceiling, and higher.

synchronizedNavigableSet() Method Syntax

The syntax for the synchronizedNavigableSet() method is as follows:

public static <T> NavigableSet<T> synchronizedNavigableSet(NavigableSet<T> s)

Parameters:

  • s: The navigable set to be wrapped in a synchronized view.

Returns:

  • A synchronized (thread-safe) navigable set backed by the specified navigable set.

Throws:

  • NullPointerException if the specified navigable set is null.

Examples

Basic Usage of synchronizedNavigableSet()

The following example demonstrates how to use the synchronizedNavigableSet() method to create a synchronized view of a navigable set.

Example

import java.util.Collections;
import java.util.Iterator;
import java.util.NavigableSet;
import java.util.TreeSet;

public class SynchronizedNavigableSetExample {
    public static void main(String[] args) {
        // Create a regular TreeSet
        NavigableSet<String> set = new TreeSet<>();
        set.add("Apple");
        set.add("Banana");
        set.add("Cherry");

        // Create a synchronized (thread-safe) navigable set backed by the TreeSet
        NavigableSet<String> synchronizedSet = Collections.synchronizedNavigableSet(set);

        // Display the synchronized navigable set
        System.out.println("Synchronized Navigable Set: " + synchronizedSet);

        // Use synchronized block for iteration to ensure thread safety
        synchronized (synchronizedSet) {
            Iterator<String> iterator = synchronizedSet.iterator();
            while (iterator.hasNext()) {
                System.out.println("Element: " + iterator.next());
            }
        }

        // Perform navigable operations
        synchronized (synchronizedSet) {
            System.out.println("Lower than Banana: " + synchronizedSet.lower("Banana"));
            System.out.println("Floor of Banana: " + synchronizedSet.floor("Banana"));
            System.out.println("Ceiling of Banana: " + synchronizedSet.ceiling("Banana"));
            System.out.println("Higher than Banana: " + synchronizedSet.higher("Banana"));
        }

        // Adding and removing elements in a synchronized navigable set
        synchronized (synchronizedSet) {
            synchronizedSet.add("Date");
            synchronizedSet.remove("Apple");
        }

        // Display the modified synchronized navigable set
        System.out.println("Modified Synchronized Navigable Set: " + synchronizedSet);
    }
}

Output:

Synchronized Navigable Set: [Apple, Banana, Cherry]
Element: Apple
Element: Banana
Element: Cherry
Lower than Banana: Apple
Floor of Banana: Banana
Ceiling of Banana: Banana
Higher than Banana: Cherry
Modified Synchronized Navigable Set: [Banana, Cherry, Date]

Using synchronizedNavigableSet() with Custom Classes

You can also use the synchronizedNavigableSet() method with sets containing instances of custom classes.

Example

import java.util.Collections;
import java.util.Iterator;
import java.util.NavigableSet;
import java.util.TreeSet;

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

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

    @Override
    public int compareTo(Student other) {
        return Integer.compare(this.age, other.age);
    }

    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

public class CustomSynchronizedNavigableSetExample {
    public static void main(String[] args) {
        // Create a navigable set of students
        NavigableSet<Student> students = new TreeSet<>();
        students.add(new Student("Amit", 20));
        students.add(new Student("Neha", 22));
        students.add(new Student("Raj", 19));

        // Create a synchronized (thread-safe) navigable set backed by the student set
        NavigableSet<Student> synchronizedStudents = Collections.synchronizedNavigableSet(students);

        // Display the synchronized student navigable set
        System.out.println("Synchronized Student Navigable Set: " + synchronizedStudents);

        // Use synchronized block for iteration to ensure thread safety
        synchronized (synchronizedStudents) {
            Iterator<Student> iterator = synchronizedStudents.iterator();
            while (iterator.hasNext()) {
                System.out.println("Student: " + iterator.next());
            }
        }

        // Perform navigable operations
        synchronized (synchronizedStudents) {
            System.out.println("Lower than Neha: " + synchronizedStudents.lower(new Student("", 22)));
            System.out.println("Floor of Neha: " + synchronizedStudents.floor(new Student("", 22)));
            System.out.println("Ceiling of Neha: " + synchronizedStudents.ceiling(new Student("", 22)));
            System.out.println("Higher than Neha: " + synchronizedStudents.higher(new Student("", 22)));
        }

        // Modifying the synchronized navigable set
        synchronized (synchronizedStudents) {
            synchronizedStudents.add(new Student("Vikram", 21));
            synchronizedStudents.remove(new Student("Amit", 20));
        }

        // Display the modified synchronized student navigable set
        System.out.println("Modified Synchronized Student Navigable Set: " + synchronizedStudents);
    }
}

Output:

Synchronized Student Navigable Set: [Raj (19), Amit (20), Neha (22)]
Student: Raj (19)
Student: Amit (20)
Student: Neha (22)
Lower than Neha: Amit (20)
Floor of Neha: Neha (22)
Ceiling of Neha: Neha (22)
Higher than Neha: null
Modified Synchronized Student Navigable Set: [Raj (19), Vikram (21), Neha (22)]

Explanation:

  1. Synchronized View: The synchronizedNavigableSet() method returns a synchronized view of the specified navigable set, ensuring thread-safe access.
  2. Synchronized Block: When iterating over the synchronized navigable set, a synchronized block is used to avoid concurrent modification exceptions and ensure thread safety.
  3. Custom Class: The method works with custom class instances, allowing you to create synchronized navigable sets with user-defined objects.
  4. Navigable Operations: The example demonstrates the use of navigable operations such as lower, floor, ceiling, and higher within the synchronized block.

Real-World Use Case

Thread-Safe Access to a Shared Sorted Resource

In real-world applications, the synchronizedNavigableSet() method can be used to manage thread-safe access to shared resources, such as a set of sorted data in a multi-threaded environment.

Example

Imagine a scenario where you need to manage a shared set of sorted tasks in a concurrent application.

import java.util.Collections;
import java.util.Iterator;
import java.util.NavigableSet;
import java.util.TreeSet;

class Task implements Comparable<Task> {
    String description;
    int priority;

    Task(String description, int priority) {
        this.description = description;
        this.priority = priority;
    }

    @Override
    public int compareTo(Task other) {
        return Integer.compare(this.priority, other.priority);
    }

    @Override
    public String toString() {
        return description + " (Priority: " + priority + ")";
    }
}

public class TaskManager {
    private final NavigableSet<Task> tasks;

    public TaskManager() {
        // Initialize the task set and wrap it in a synchronized view
        this.tasks = Collections.synchronizedNavigableSet(new TreeSet<>());
    }

    public void addTask(Task task) {
        synchronized (tasks) {
            tasks.add(task);
        }
    }

    public void removeTask(Task task) {
        synchronized (tasks) {
            tasks.remove(task);
        }
    }

    public void displayTasks() {
        // Use synchronized block for iteration to ensure thread safety
        synchronized (tasks) {
            Iterator<Task> iterator = tasks.iterator();
            while (iterator.hasNext()) {
                System.out.println("Task: " + iterator.next());
            }
        }
    }

    public Task getHighestPriorityTask() {
        synchronized (tasks) {
            return tasks.pollFirst();
        }
    }

    public static void main(String[] args) {
        TaskManager taskManager = new TaskManager();

        // Add tasks to the manager
        taskManager.addTask(new Task("Review code", 2));
        taskManager.addTask(new Task("Write tests", 1));
        taskManager.addTask(new Task("Deploy application", 3));

        // Display all tasks
        System.out.println("Task List:");
        taskManager.displayTasks();

        // Get and remove the highest priority task
        Task highestPriorityTask = taskManager.getHighestPriorityTask();
        System.out.println("\nHighest Priority Task: " + highestPriorityTask);

        // Display remaining tasks
        System.out.println("\nRemaining Task List:");
        taskManager.displayTasks();
    }
}

Output:

Compilation failed.

Explanation:

  1. Task Manager: The TaskManager class manages a navigable set of tasks, ensuring thread-safe access by wrapping the set in a synchronized view.
  2. Concurrent Environment: The synchronizedNavigableSet() method is used to synchronize access to the shared set, preventing race conditions and ensuring data consistency.
  3. Priority Management: The example demonstrates the use of navigable operations to manage task priorities and retrieve the highest-priority task.

Conclusion

The Collections.synchronizedNavigableSet() method is a powerful utility for creating synchronized (thread-safe) navigable sets in Java. By providing a simple way to wrap navigable sets with synchronized access, it enhances the flexibility and safety of your code in concurrent environments. This method is particularly valuable in scenarios where you need to manage shared resources or ensure thread-safe access to sorted data, improving the robustness and maintainability of your Java applications.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top