ZetCode

Java Collections.unmodifiableSortedSet

上次修改时间:2025 年 4 月 20 日

Collections.unmodifiableSortedSet 方法是 Java Collections Framework 的一部分。它返回指定 SortedSet 的不可修改视图。此视图是只读的,并且在尝试修改时会抛出异常。

当您需要提供对数据的只读访问权限时,不可修改的集合非常有用。它们有助于强制实现不可变性并防止意外修改。如果您保留对原始已排序集合的引用,则原始已排序集合仍然可以被修改。

SortedSet 和不可修改集合概述

SortedSet 是一个 Set,它按升序维护其元素。排序可以是自然的,也可以由 Comparator 确定。TreeSet 是主要的实现。

不可修改的集合是阻止修改操作的包装器。当尝试修改时,它们会抛出 UnsupportedOperationException 异常。如果您有引用,则原始集合仍然可修改。

基本 unmodifiableSortedSet 示例

此示例演示了如何创建 TreeSet 的不可修改视图。我们首先创建一个可变的 TreeSet,然后使用 unmodifiableSortedSet 包装它。该示例展示了允许和不允许的操作。

BasicUnmodifiableSortedSet.java
package com.zetcode;

import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;

public class BasicUnmodifiableSortedSet {

    public static void main(String[] args) {
        
        // Create a mutable sorted set
        SortedSet<String> mutableSet = new TreeSet<>();
        mutableSet.add("Apple");
        mutableSet.add("Banana");
        mutableSet.add("Cherry");
        
        // Create unmodifiable view
        SortedSet<String> unmodifiableSet = 
            Collections.unmodifiableSortedSet(mutableSet);
        
        System.out.println("Original set: " + mutableSet);
        System.out.println("Unmodifiable view: " + unmodifiableSet);
        
        // Allowed operation: reading elements
        System.out.println("First element: " + unmodifiableSet.first());
        
        try {
            // Attempt to modify unmodifiable view
            unmodifiableSet.add("Date");
        } catch (UnsupportedOperationException e) {
            System.out.println("\nCannot modify unmodifiable set: " + e);
        }
        
        // Original set can still be modified
        mutableSet.add("Date");
        System.out.println("After modifying original: " + unmodifiableSet);
    }
}

此代码创建一个 TreeSet 并使用 unmodifiableSortedSet 包装它。我们演示了读取操作有效,但修改尝试会抛出异常。对原始集合的更改通过不可修改的视图可见。

输出显示了初始集合、读取操作、尝试修改时的异常,以及对原始集合的更改如何反映在视图中。

创建真正的不可变 SortedSet

要创建一个完全不可变的已排序集合,我们可以将 unmodifiableSortedSet 与一步初始化结合起来。此方法防止任何修改,因为没有对原始集合的引用。

ImmutableSortedSet.java
package com.zetcode;

import java.util.Set;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;

public class ImmutableSortedSet {

    public static void main(String[] args) {
        
        // Create truly immutable sorted set
        SortedSet<Integer> immutableSet = Collections.unmodifiableSortedSet(
            new TreeSet<>(Set.of(5, 2, 8, 1, 9))
        );
        
        System.out.println("Immutable set: " + immutableSet);
        
        // All read operations work
        System.out.println("First: " + immutableSet.first());
        System.out.println("Last: " + immutableSet.last());
        System.out.println("Subset (3-7): " + 
            immutableSet.subSet(3, 7));
        
        try {
            // All modification attempts fail
            immutableSet.add(10);
        } catch (UnsupportedOperationException e) {
            System.out.println("\nCannot modify immutable set: " + e);
        }
    }
}

此示例通过不保留对原始 TreeSet 的引用来创建一个完全不可变的已排序集合。集合用值初始化并立即被包装。对原始集合或视图都无法进行修改。

输出演示了成功的读取操作以及尝试修改时预期的异常。这种模式对于创建常量很有用。

将 Comparator 与 unmodifiableSortedSet 一起使用

此示例展示了如何将自定义 ComparatorunmodifiableSortedSet 一起使用。比较器定义了集合中元素的排序。不可修改的视图维护此排序。

ComparatorSortedSet.java
package com.zetcode;

import java.util.Collections;
import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.List;

public class ComparatorSortedSet {

    public static void main(String[] args) {
        
        // Create comparator for reverse order
        Comparator<String> reverseComparator = 
            Comparator.reverseOrder();
        
        // Create sorted set with comparator
        SortedSet<String> mutableSet = 
            new TreeSet<>(reverseComparator);
        mutableSet.addAll(List.of("Apple", "Banana", "Cherry"));
        
        // Create unmodifiable view
        SortedSet<String> unmodifiableSet = 
            Collections.unmodifiableSortedSet(mutableSet);
        
        System.out.println("Reverse ordered set: " + unmodifiableSet);
        
        // Comparator is preserved
        System.out.println("Comparator: " + 
            unmodifiableSet.comparator());
        
        // All sorted set operations work
        System.out.println("First element: " + unmodifiableSet.first());
        System.out.println("Head set (before 'Banana'): " + 
            unmodifiableSet.headSet("Banana"));
    }
}

此代码演示了将自定义比较器与不可修改的已排序集合一起使用。我们创建一个反向顺序比较器并使用它来初始化 TreeSet。不可修改的视图维护排序和比较器引用。

输出显示了反向排序的元素、保留的比较器以及在不可修改视图上正确运行的标准已排序集合操作。

使用子集

SortedSet 提供了创建子集的方法。此示例展示了这些方法如何与不可修改的已排序集合一起使用。子集本身也是不可修改的。

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> numbers = new TreeSet<>();
        Collections.addAll(numbers, 1, 2, 3, 4, 5, 6, 7, 8, 9);
        
        SortedSet<Integer> unmodifiableNumbers = 
            Collections.unmodifiableSortedSet(numbers);
        
        // Create subsets from unmodifiable set
        SortedSet<Integer> headSet = unmodifiableNumbers.headSet(5);
        SortedSet<Integer> tailSet = unmodifiableNumbers.tailSet(5);
        SortedSet<Integer> subSet = unmodifiableNumbers.subSet(3, 7);
        
        System.out.println("Original: " + unmodifiableNumbers);
        System.out.println("Head set (elements < 5): " + headSet);
        System.out.println("Tail set (elements >= 5): " + tailSet);
        System.out.println("Sub set (elements 3-6): " + subSet);
        
        try {
            // Attempt to modify subset
            headSet.add(0);
        } catch (UnsupportedOperationException e) {
            System.out.println("\nCannot modify subset: " + e);
        }
        
        // Changes to original are reflected in subsets
        numbers.add(0);
        System.out.println("After adding to original:");
        System.out.println("Head set now: " + headSet);
    }
}

此示例演示了对不可修改的已排序集合的子集操作。我们创建了头、尾和任意子集。这些子集也是不可修改的,并反映了对原始集合的更改。

输出显示了子集,并演示了它们无法直接修改。它还显示了修改原始集合如何影响子集。

性能注意事项

此示例考察了 unmodifiableSortedSet 的性能特征。包装器对操作添加了最小的开销,因为它只是一个视图。所有操作都委托给底层集合。

PerformanceTest.java
package com.zetcode;

import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;

public class PerformanceTest {

    public static void main(String[] args) {
        
        // Create large sorted set
        SortedSet<Integer> largeSet = new TreeSet<>();
        for (int i = 0; i < 1000000; i++) {
            largeSet.add(i);
        }
        
        // Create unmodifiable view
        SortedSet<Integer> unmodifiableSet = 
            Collections.unmodifiableSortedSet(largeSet);
        
        // Time operations on original
        long start = System.nanoTime();
        boolean contains = largeSet.contains(999999);
        long originalTime = System.nanoTime() - start;
        
        // Time same operation on unmodifiable view
        start = System.nanoTime();
        contains = unmodifiableSet.contains(999999);
        long unmodifiableTime = System.nanoTime() - start;
        
        System.out.println("Original set contains operation: " + 
            originalTime + " ns");
        System.out.println("Unmodifiable set contains operation: " + 
            unmodifiableTime + " ns");
        System.out.println("Overhead: " + 
            (unmodifiableTime - originalTime) + " ns");
    }
}

此代码比较了对大型原始已排序集合及其不可修改视图执行操作的性能。我们测量了对两者执行包含操作的时间。差异显示了包装器的最小开销。

输出表明不可修改的包装器对操作增加了可忽略的开销。对于读取操作,性能与直接使用原始集合基本相同。

实际用例

此示例展示了 unmodifiableSortedSet 在一个 API 中的实际应用,该 API 提供对已排序数据的只读访问。API 返回一个不可修改的视图,以防止客户端代码修改内部数据。

TemperatureService.java
package com.zetcode;

import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;

public class TemperatureService {

    private final SortedSet<Double> temperatures = new TreeSet<>();
    
    public void addTemperature(double temp) {
        temperatures.add(temp);
    }
    
    public SortedSet<Double> getTemperatures() {
        return Collections.unmodifiableSortedSet(temperatures);
    }
    
    public static void main(String[] args) {
        TemperatureService service = new TemperatureService();
        
        // Add some data
        service.addTemperature(23.5);
        service.addTemperature(19.2);
        service.addTemperature(21.8);
        service.addTemperature(25.1);
        service.addTemperature(18.9);
        
        // Get unmodifiable view of temperatures
        SortedSet<Double> temps = service.getTemperatures();
        System.out.println("All temperatures (sorted): " + temps);
        System.out.println("Highest temperature: " + temps.last());
        System.out.println("Lowest temperature: " + temps.first());
        
        try {
            // Client cannot modify the set
            temps.add(22.0);
        } catch (UnsupportedOperationException e) {
            System.out.println("\nClient cannot modify temperatures: " + e);
        }
    }
}

此示例演示了一个温度服务,该服务维护一个已排序的温度读数集合。该服务通过不可修改的视图向客户端提供只读访问权限。客户端可以看到数据,但不能修改它。

输出显示了已排序的温度,并演示了客户端代码无法修改集合。这种模式在需要安全地公开内部集合的 API 中很常见。

来源

Java Collections.unmodifiableSortedSet 文档

在本教程中,我们深入探讨了 Collections.unmodifiableSortedSet。我们涵盖了基本用法、不可变性模式、比较器、子集、性能和实际应用。此方法对于创建已排序数据的只读视图非常有用。

作者

我叫 Jan Bodnar,是一名经验丰富的程序员,在这一领域拥有多年的经验。我从 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和八本电子书。凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程