Complete Java Collections.synchronizedSortedSet tutorial with examples. Learn how to use synchronized sorted sets in Java.
Last modified: April 20, 2025
The Collections.synchronizedSortedSet method provides thread-safe access to sorted sets. It returns a synchronized (thread-safe) sorted set backed by the specified sorted set. This wrapper ensures safe concurrent access.
In multi-threaded environments, direct access to collections can cause issues. The synchronized wrapper prevents data corruption by synchronizing all operations. It’s part of Java’s Collections Framework utility methods.
A SortedSet maintains elements in ascending order according to their natural ordering or a comparator. However, standard implementations like TreeSet are not thread-safe. Synchronization is needed for multi-threaded access.
The Collections.synchronizedSortedSet method solves this by wrapping the set. All access to the returned set is synchronized on the set itself. This prevents concurrent modification issues.
This example demonstrates creating a synchronized sorted set from a TreeSet. We show basic operations like adding elements and iteration. The synchronized wrapper ensures thread safety.
BasicSynchronizedSortedSet.java
package com.zetcode;
import java.util.Collections; import java.util.SortedSet; import java.util.TreeSet;
public class BasicSynchronizedSortedSet {
public static void main(String[] args) {
// Create a regular TreeSet
SortedSet<String> treeSet = new TreeSet<>();
// Wrap it in a synchronized sorted set
SortedSet<String> syncSet = Collections.synchronizedSortedSet(treeSet);
// Add elements
syncSet.add("Apple");
syncSet.add("Banana");
syncSet.add("Cherry");
// Iterate (must synchronize manually for iteration)
synchronized(syncSet) {
for (String fruit : syncSet) {
System.out.println(fruit);
}
}
System.out.println("Size: " + syncSet.size());
}
}
This code creates a synchronized wrapper around a TreeSet. All operations on the set are now thread-safe. Note that iteration requires explicit synchronization. The output shows the elements in sorted order.
The synchronized wrapper ensures safe concurrent access. However, compound operations still need external synchronization. The example demonstrates proper iteration synchronization.
This example shows how multiple threads can safely access a synchronized sorted set. We create two threads that concurrently add elements. The synchronized wrapper prevents corruption.
MultiThreadedSortedSet.java
package com.zetcode;
import java.util.Collections; import java.util.SortedSet; import java.util.TreeSet;
public class MultiThreadedSortedSet {
public static void main(String[] args) throws InterruptedException {
SortedSet<Integer> syncSet = Collections.synchronizedSortedSet(
new TreeSet<>());
// Create and start two threads
Thread t1 = new Thread(() -> addNumbers(syncSet, 0, 50));
Thread t2 = new Thread(() -> addNumbers(syncSet, 50, 100));
t1.start();
t2.start();
t1.join();
t2.join();
// Verify all numbers were added
System.out.println("Total elements: " + syncSet.size());
System.out.println("First: " + syncSet.first());
System.out.println("Last: " + syncSet.last());
}
private static void addNumbers(SortedSet<Integer> set, int start, int end) {
for (int i = start; i < end; i++) {
set.add(i);
}
}
}
This example demonstrates thread-safe concurrent access. Two threads add numbers to the set simultaneously. The synchronized wrapper prevents race conditions. The final output shows all numbers were added correctly.
Note that while individual operations are thread-safe, compound operations may need additional synchronization. The example shows proper thread coordination using join().
This example demonstrates using a custom comparator with a synchronized sorted set. We create a case-insensitive sorted set and wrap it for thread safety. The comparator defines the sorting order.
ComparatorSortedSet.java
package com.zetcode;
import java.util.Collections; import java.util.Comparator; import java.util.SortedSet; import java.util.TreeSet;
public class ComparatorSortedSet {
public static void main(String[] args) {
// Create comparator for case-insensitive sorting
Comparator<String> ignoreCase = String.CASE_INSENSITIVE_ORDER;
// Create sorted set with comparator
SortedSet<String> treeSet = new TreeSet<>(ignoreCase);
// Wrap in synchronized set
SortedSet<String> syncSet = Collections.synchronizedSortedSet(treeSet);
// Add mixed-case elements
syncSet.add("apple");
syncSet.add("Banana");
syncSet.add("CHERRY");
syncSet.add("dATE");
// Iterate (with synchronization)
synchronized(syncSet) {
for (String fruit : syncSet) {
System.out.println(fruit);
}
}
}
}
This code creates a case-insensitive sorted set and wraps it for thread safety. The comparator ensures proper ordering regardless of case. The synchronized wrapper makes all operations thread-safe.
The output shows elements sorted case-insensitively. The synchronization ensures safe access even if multiple threads modify the set concurrently.
This example demonstrates bulk operations on a synchronized sorted set. We show addAll, removeAll, and retainAll operations. Each operation is automatically synchronized.
BulkOperationsSortedSet.java
package com.zetcode;
import java.util.Arrays; import java.util.Collections; import java.util.SortedSet; import java.util.TreeSet;
public class BulkOperationsSortedSet {
public static void main(String[] args) {
SortedSet<String> syncSet = Collections.synchronizedSortedSet(
new TreeSet<>());
// Add multiple elements
syncSet.addAll(Arrays.asList("Apple", "Banana", "Cherry", "Date"));
System.out.println("After addAll: " + syncSet);
// Remove multiple elements
syncSet.removeAll(Arrays.asList("Banana", "Date"));
System.out.println("After removeAll: " + syncSet);
// Retain only specified elements
syncSet.retainAll(Arrays.asList("Apple", "Cherry", "Fig"));
System.out.println("After retainAll: " + syncSet);
// Check contains all
boolean hasAll = syncSet.containsAll(Arrays.asList("Apple", "Cherry"));
System.out.println("Contains all: " + hasAll);
}
}
This example shows bulk operations on a synchronized sorted set. Each operation is thread-safe due to the synchronization wrapper. The output demonstrates how each operation modifies the set.
Bulk operations are particularly useful when working with groups of elements. The synchronized wrapper ensures these operations remain atomic and thread-safe.
This example demonstrates subset operations and proper iteration techniques. We show subSet, headSet, and tailSet methods. Iteration requires explicit synchronization.
SubsetOperations.java
package com.zetcode;
import java.util.Collections; import java.util.SortedSet; import java.util.TreeSet;
public class SubsetOperations {
public static void main(String[] args) {
SortedSet<Integer> syncSet = Collections.synchronizedSortedSet(
new TreeSet<>());
// Add numbers 1-100
for (int i = 1; i <= 100; i++) {
syncSet.add(i);
}
// Get subset 40-60
SortedSet<Integer> subSet = syncSet.subSet(40, 60);
System.out.println("Subset 40-60 size: " + subSet.size());
// Get headSet (elements < 25)
SortedSet<Integer> headSet = syncSet.headSet(25);
System.out.println("HeadSet <25 size: " + headSet.size());
// Get tailSet (elements >= 75)
SortedSet<Integer> tailSet = syncSet.tailSet(75);
System.out.println("TailSet >=75 size: " + tailSet.size());
// Iterate subset (with synchronization)
synchronized(syncSet) {
for (Integer num : subSet) {
System.out.print(num + " ");
}
}
}
}
This example demonstrates subset operations on a synchronized sorted set. The subSet, headSet, and tailSet methods create views of portions of the original set. These views are also synchronized.
The output shows the sizes of different subsets. The synchronized iteration ensures thread-safe access during traversal. Subset operations are powerful for working with ranges of sorted data.
This example compares performance between synchronized and unsynchronized sorted sets. We measure operation times for both to show synchronization overhead. The test demonstrates when synchronization is necessary.
PerformanceComparison.java
package com.zetcode;
import java.util.Collections; import java.util.SortedSet; import java.util.TreeSet;
public class PerformanceComparison {
public static void main(String[] args) {
final int ELEMENTS = 100000;
// Unsynchronized set
SortedSet<Integer> treeSet = new TreeSet<>();
long start = System.currentTimeMillis();
for (int i = 0; i < ELEMENTS; i++) {
treeSet.add(i);
}
long unsyncTime = System.currentTimeMillis() - start;
// Synchronized set
SortedSet<Integer> syncSet = Collections.synchronizedSortedSet(
new TreeSet<>());
start = System.currentTimeMillis();
for (int i = 0; i < ELEMENTS; i++) {
syncSet.add(i);
}
long syncTime = System.currentTimeMillis() - start;
System.out.println("Unsynchronized add time: " + unsyncTime + "ms");
System.out.println("Synchronized add time: " + syncTime + "ms");
System.out.println("Overhead: " +
((double)(syncTime - unsyncTime)/unsyncTime * 100) + "%");
}
}
This code compares performance between synchronized and unsynchronized sorted sets. The test adds many elements to both sets and measures the time taken. The output shows the synchronization overhead percentage.
Synchronization adds overhead but is necessary for thread safety. In single- threaded scenarios, prefer unsynchronized collections. The example helps understand the performance trade-offs.
Java Collections.synchronizedSortedSet Documentation
In this article, we’ve explored Collections.synchronizedSortedSet in depth. We’ve covered basic usage, multi-threading, comparators, bulk operations, subsets, and performance. Understanding these concepts is crucial for thread-safe sorted set operations.
My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.
List all Java tutorials.