ZetCode

Java Collections.checkedSortedSet 方法

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

Collections.checkedSortedSet 方法是 Java Collections Framework 的一部分。它为 SortedSet 实现提供运行时类型安全。此方法返回指定排序集合的动态类型安全视图。

任何尝试插入错误类型元素的操作都会立即导致 ClassCastException。 这有助于及早检测编程错误,而不是允许它们传播。 如果指定的集合是可序列化的,则返回的集合将是可序列化的。

Collections.checkedSortedSet 概述

当处理遗留代码或无法在编译时保证类型安全时,checkedSortedSet 方法特别有用。 它包装现有的 SortedSet 并在运行时强制执行类型检查。

该方法的签名为:static <E> SortedSet<E> checkedSortedSet(SortedSet<E> s, Class<E> type)。 它接受要包装的排序集合以及它应包含的元素的类型。

checkedSortedSet 的基本用法

此示例演示了 Collections.checkedSortedSet 的基本用法。 我们创建一个字符串 TreeSet 并使用类型安全视图包装它。 该示例显示了有效和无效的操作。

BasicCheckedSortedSet.java
package com.zetcode;

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

public class BasicCheckedSortedSet {

    public static void main(String[] args) {
        
        SortedSet<String> names = new TreeSet<>();
        names.add("Alice");
        names.add("Bob");
        
        // Create type-safe view
        SortedSet<String> checkedNames = 
            Collections.checkedSortedSet(names, String.class);
        
        // Valid operation
        checkedNames.add("Charlie");
        System.out.println("Valid addition: " + checkedNames);
        
        try {
            // Invalid operation - adding wrong type
            checkedNames.add(42); // Integer instead of String
        } catch (ClassCastException e) {
            System.out.println("\nCaught exception: " + e.getMessage());
        }
    }
}

此代码创建一个字符串的 TreeSet 并使用 Collections.checkedSortedSet 包装它。 类型安全视图允许添加有效的 String 元素,但会为无效类型抛出 ClassCastException

输出显示成功添加了“Charlie”,以及尝试添加 Integer 时捕获的异常。 这演示了运行时类型检查。

使用遗留代码

在处理不使用泛型的遗留代码时,checkedSortedSet 特别有用。 此示例演示了如何防止此类场景中的类型不安全操作。

LegacyCodeProtection.java
package com.zetcode;

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

public class LegacyCodeProtection {

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static void main(String[] args) {
        
        // Legacy code without generics
        SortedSet rawSet = new TreeSet();
        rawSet.add("Apple");
        rawSet.add("Banana");
        
        // Create type-safe view
        SortedSet<String> safeSet = 
            Collections.checkedSortedSet(rawSet, String.class);
        
        // Pass to legacy method
        legacyMethod(rawSet);
        
        System.out.println("After legacy method: " + safeSet);
        
        try {
            // Now try to use the safe view
            safeSet.add("Cherry"); // Valid
            safeSet.add(123); // Invalid
        } catch (ClassCastException e) {
            System.out.println("\nCaught exception: " + e.getMessage());
        }
    }
    
    @SuppressWarnings("rawtypes")
    private static void legacyMethod(SortedSet set) {
        // Legacy method might add wrong types
        set.add(42); // This will succeed in raw set
    }
}

此示例演示了 checkedSortedSet 如何帮助识别遗留代码中的类型安全问题。 原始集合允许添加任何类型,但是类型安全视图会在使用时捕获无效的添加。

输出显示遗留方法成功地将 Integer 添加到原始集合,但尝试通过类型安全视图添加 Integer 会抛出异常。 这有助于定位类型安全问题。

与其他 Collections 方法结合使用

此示例显示了如何将 checkedSortedSet 与其他 Collections 实用程序方法结合使用。 我们创建一个排序集合的不可修改的类型安全视图。

CombinedCollectionsMethods.java
package com.zetcode;

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

public class CombinedCollectionsMethods {

    public static void main(String[] args) {
        
        SortedSet<Integer> numbers = new TreeSet<>();
        numbers.add(10);
        numbers.add(20);
        numbers.add(30);
        
        // Create type-safe and unmodifiable view
        SortedSet<Integer> safeUnmodifiableSet = Collections.unmodifiableSortedSet(
            Collections.checkedSortedSet(numbers, Integer.class));
        
        System.out.println("Initial set: " + safeUnmodifiableSet);
        
        try {
            // Attempt modification through unmodifiable view
            safeUnmodifiableSet.add(40);
        } catch (UnsupportedOperationException e) {
            System.out.println("\nCaught UnsupportedOperationException: " + 
                e.getMessage());
        }
        
        try {
            // Attempt to add wrong type to original set
            numbers.add("Not a number");
        } catch (ClassCastException e) {
            System.out.println("\nCaught ClassCastException: " + e.getMessage());
        }
    }
}

此代码将 checkedSortedSetunmodifiableSortedSet 结合使用,以创建一个既类型安全又不可修改的视图。 该示例演示了类型安全和不可变性保护。

输出显示尝试通过不可修改的视图修改集合的尝试被捕获,并且尝试将错误类型的元素添加到原始集合的尝试也被捕获。 这演示了分层集合保护。

使用自定义比较器

checkedSortedSet 可与使用自定义比较器的排序集合一起使用。 此示例演示了具有不区分大小写的字符串比较器的排序集合中的类型检查。

CustomComparatorExample.java
package com.zetcode;

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

public class CustomComparatorExample {

    public static void main(String[] args) {
        
        // Create sorted set with case-insensitive comparator
        SortedSet<String> caseInsensitiveSet = new TreeSet<>(
            String.CASE_INSENSITIVE_ORDER);
        
        caseInsensitiveSet.add("Apple");
        caseInsensitiveSet.add("banana");
        
        // Create type-safe view
        SortedSet<String> checkedSet = 
            Collections.checkedSortedSet(caseInsensitiveSet, String.class);
        
        System.out.println("Original set: " + checkedSet);
        
        // Adding with different case works due to comparator
        checkedSet.add("APPLE");
        System.out.println("After adding 'APPLE': " + checkedSet);
        
        try {
            // Adding wrong type fails
            checkedSet.add(42);
        } catch (ClassCastException e) {
            System.out.println("\nCaught ClassCastException: " + e.getMessage());
        }
    }
}

此示例表明 checkedSortedSet 可与使用自定义比较器的排序集合一起使用。 类型检查的执行独立于排序逻辑。

输出表明不区分大小写的比较器允许添加“APPLE”(尽管它不会出现两次,因为它等于“Apple”),而尝试添加 Integer 会被类型检查捕获。

使用 checkedSortedSet 进行序列化

如果支持集合是可序列化的,则 checkedSortedSet 返回的集合是可序列化的。 此示例演示了类型安全排序集合的序列化和反序列化。

SerializationExample.java
package com.zetcode;

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

public class SerializationExample {

    public static void main(String[] args) {
        String filename = "checkedset.ser";
        
        // Create and serialize a checked sorted set
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(filename))) {
            
            SortedSet<Double> numbers = new TreeSet<>();
            numbers.add(3.14);
            numbers.add(2.71);
            
            SortedSet<Double> checkedNumbers = 
                Collections.checkedSortedSet(numbers, Double.class);
            
            oos.writeObject(checkedNumbers);
            System.out.println("Serialized set: " + checkedNumbers);
            
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // Deserialize the checked sorted set
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(filename))) {
            
            @SuppressWarnings("unchecked")
            SortedSet<Double> deserialized = (SortedSet<Double>) ois.readObject();
            
            System.out.println("Deserialized set: " + deserialized);
            
            // Verify type safety still works
            try {
                deserialized.add("Not a number");
            } catch (ClassCastException e) {
                System.out.println("\nCaught ClassCastException: " + 
                    e.getMessage());
            }
            
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了类型安全包装器在序列化和反序列化后保持其类型检查功能。 序列化的集合保留其元素和类型安全约束。

输出显示了序列化之前和反序列化之后的集合,以及尝试添加无效类型时捕获的异常。 这证实了类型安全通过序列化得以维护。

性能注意事项

虽然 checkedSortedSet 提供了有价值的类型安全,但它确实增加了一点性能开销。 此示例演示了如何衡量类型检查包装的影响。

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 ELEMENT_COUNT = 100000;
        final int TEST_RUNS = 100;
        
        SortedSet<Integer> regularSet = new TreeSet<>();
        SortedSet<Integer> checkedSet = 
            Collections.checkedSortedSet(new TreeSet<>(), Integer.class);
        
        // Test regular set performance
        long regularTime = timeAddOperations(regularSet, ELEMENT_COUNT, TEST_RUNS);
        
        // Test checked set performance
        long checkedTime = timeAddOperations(checkedSet, ELEMENT_COUNT, TEST_RUNS);
        
        System.out.println("Regular set time: " + regularTime + " ms");
        System.out.println("Checked set time: " + checkedTime + " ms");
        System.out.println("Overhead: " + 
            ((double)(checkedTime - regularTime)/regularTime * 100) + "%");
    }
    
    private static long timeAddOperations(SortedSet<Integer> set, 
            int count, int runs) {
        long totalTime = 0;
        
        for (int i = 0; i < runs; i++) {
            set.clear();
            long start = System.currentTimeMillis();
            
            for (int j = 0; j < count; j++) {
                set.add(j);
            }
            
            totalTime += System.currentTimeMillis() - start;
        }
        
        return totalTime / runs;
    }
}

此代码比较了常规 TreeSet 与类型安全包装版本的性能。 该测试测量了将元素添加到两个集合所花费的时间,并对多次运行取平均值以提高准确性。

输出显示了绝对时间以及类型检查引入的百分比开销。 虽然开销通常很小,但在对性能至关重要的应用程序中需要考虑它。

来源

Java Collections.checkedSortedSet 文档

在本文中,我们深入探讨了 Collections.checkedSortedSet 方法。 我们涵盖了基本用法、遗留代码集成、与其他实用程序的组合、自定义比较器、序列化和性能。

作者

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

列出所有Java教程