ZetCode

Java Collections.unmodifiableCollection

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

Collections.unmodifiableCollection 方法是 Java 集合框架中的一个实用方法。它返回指定集合的不可修改视图。此视图阻止对底层集合进行修改。

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

Collections.unmodifiableCollection 概述

unmodifiableCollection 方法是 java.util.Collections 类的一部分。它接受一个集合作为输入,并返回该集合的不可修改视图。任何修改返回集合的尝试都将抛出 UnsupportedOperationException

此方法是 Collections 类中几个不可修改集合包装器之一。List、Set、Map 和其他集合类型也存在类似的方法。返回的视图是实时的——对原始集合的更改通过它可见。

基本 unmodifiableCollection 示例

此示例演示了 Collections.unmodifiableCollection 的基本用法。我们创建一个可修改的 ArrayList,然后获取它的不可修改视图。我们展示了原始集合仍然可以被修改,但视图不能。

BasicUnmodifiableCollection.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class BasicUnmodifiableCollection {

    public static void main(String[] args) {
        
        Collection<String> modifiable = new ArrayList<>();
        modifiable.add("Apple");
        modifiable.add("Banana");
        
        Collection<String> unmodifiable = 
            Collections.unmodifiableCollection(modifiable);
        
        System.out.println("Original collection: " + modifiable);
        System.out.println("Unmodifiable view: " + unmodifiable);
        
        // Modify original - change is visible in unmodifiable view
        modifiable.add("Cherry");
        System.out.println("After adding to original: " + unmodifiable);
        
        try {
            // Attempt to modify unmodifiable view
            unmodifiable.add("Date");
        } catch (UnsupportedOperationException e) {
            System.out.println("Cannot modify unmodifiable collection: " + e);
        }
    }
}

此代码显示了不可修改集合的基本行为。原始集合保持可修改状态,而不可修改视图在尝试修改时抛出异常。对原始集合的更改会反映在视图中。

输出结果表明,向原始集合添加元素会影响不可修改视图。但是,直接尝试修改视图会导致异常。这是预期的行为。

不同类型的不可修改集合

unmodifiableCollection 方法适用于任何 Collection 实现。此示例展示了它与不同集合类型(ArrayList、HashSet 和 LinkedList)一起使用。行为在所有实现中都一致。

DifferentCollectionTypes.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;

public class DifferentCollectionTypes {

    public static void main(String[] args) {
        
        // ArrayList example
        Collection<String> arrayList = new ArrayList<>();
        arrayList.add("Red");
        arrayList.add("Green");
        Collection<String> unmodArrayList = 
            Collections.unmodifiableCollection(arrayList);
        System.out.println("ArrayList unmodifiable: " + unmodArrayList);
        
        // HashSet example
        Collection<Integer> hashSet = new HashSet<>();
        hashSet.add(10);
        hashSet.add(20);
        Collection<Integer> unmodHashSet = 
            Collections.unmodifiableCollection(hashSet);
        System.out.println("HashSet unmodifiable: " + unmodHashSet);
        
        // LinkedList example
        Collection<Double> linkedList = new LinkedList<>();
        linkedList.add(3.14);
        linkedList.add(2.71);
        Collection<Double> unmodLinkedList = 
            Collections.unmodifiableCollection(linkedList);
        System.out.println("LinkedList unmodifiable: " + unmodLinkedList);
        
        // All throw UnsupportedOperationException on modification
        try {
            unmodArrayList.add("Blue");
        } catch (UnsupportedOperationException e) {
            System.out.println("ArrayList modification failed as expected");
        }
    }
}

此示例演示了 unmodifiableCollection 在不同的 Collection 实现中是如何统一工作的。该方法不关心具体的实现 - 它适用于任何 Collection。

输出结果显示,无论底层集合是 ArrayList、HashSet 还是 LinkedList,不可修改视图的行为都相同。所有修改尝试都会导致异常。

防御性复制 vs 不可修改视图

此示例比较了两种保护集合免受修改的方法:创建防御性副本与使用不可修改视图。每种方法都有不同的特征和用例。

DefensiveCopyVsUnmodifiable.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class DefensiveCopyVsUnmodifiable {

    public static void main(String[] args) {
        
        Collection<String> original = new ArrayList<>();
        original.add("One");
        original.add("Two");
        
        // Approach 1: Defensive copy
        Collection<String> defensiveCopy = new ArrayList<>(original);
        
        // Approach 2: Unmodifiable view
        Collection<String> unmodifiableView = 
            Collections.unmodifiableCollection(original);
        
        System.out.println("Original: " + original);
        System.out.println("Defensive copy: " + defensiveCopy);
        System.out.println("Unmodifiable view: " + unmodifiableView);
        
        // Modify original
        original.add("Three");
        
        System.out.println("\nAfter modifying original:");
        System.out.println("Original: " + original);
        System.out.println("Defensive copy unchanged: " + defensiveCopy);
        System.out.println("Unmodifiable view reflects change: " + unmodifiableView);
    }
}

此代码突出了防御性副本和不可修改视图之间的关键区别。防御性副本是一个完全独立的集合,它不会反映对原始集合的更改。不可修改视图是一个实时视图,它显示对原始集合的更改。

输出结果表明,当原始集合被修改时,防御性副本保持不变,而不可修改视图显示了新元素。根据您是想要隔离还是实时更新来选择。

方法返回中的不可修改集合

不可修改集合的一个常见用例是从方法中返回它们。此示例展示了如何在不允外部修改的情况下安全地公开内部集合。该技术有助于维护封装性。

MethodReturnExample.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

class Team {
    private Collection<String> members = new ArrayList<>();
    
    public Team() {
        members.add("Alice");
        members.add("Bob");
        members.add("Charlie");
    }
    
    // Safe way to expose the collection
    public Collection<String> getMembers() {
        return Collections.unmodifiableCollection(members);
    }
    
    // Controlled modification
    public void addMember(String name) {
        members.add(name);
    }
}

public class MethodReturnExample {

    public static void main(String[] args) {
        Team team = new Team();
        
        Collection<String> teamMembers = team.getMembers();
        System.out.println("Team members: " + teamMembers);
        
        // Attempt to modify - will throw exception
        try {
            teamMembers.add("Diana");
        } catch (UnsupportedOperationException e) {
            System.out.println("Cannot modify team members directly");
        }
        
        // Proper way to add a member
        team.addMember("Diana");
        System.out.println("After proper addition: " + team.getMembers());
    }
}

此示例演示了从类中公开集合的一个好习惯。我们没有直接返回内部集合,而是返回一个不可修改的视图。这可以防止外部代码直接修改我们的内部状态。

输出结果显示,尝试修改返回的集合会失败,而通过类的正确方法进行修改则有效。这可以保持对集合状态的控制。

嵌套的不可修改集合

此示例探讨了在使用嵌套集合时会发生什么情况。使外部集合不可修改并不会自动使包含的集合不可修改。我们需要小心处理嵌套。

NestedCollections.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class NestedCollections {

    public static void main(String[] args) {
        
        Collection<Collection<String>> nested = new ArrayList<>();
        
        Collection<String> inner1 = new ArrayList<>();
        inner1.add("A");
        inner1.add("B");
        
        Collection<String> inner2 = new ArrayList<>();
        inner2.add("X");
        inner2.add("Y");
        
        nested.add(inner1);
        nested.add(inner2);
        
        Collection<Collection<String>> unmodifiableNested = 
            Collections.unmodifiableCollection(nested);
        
        System.out.println("Original nested: " + nested);
        System.out.println("Unmodifiable nested: " + unmodifiableNested);
        
        // Can't modify outer collection
        try {
            unmodifiableNested.add(new ArrayList<>());
        } catch (UnsupportedOperationException e) {
            System.out.println("Cannot modify outer collection");
        }
        
        // But can modify inner collections!
        inner1.add("C");
        System.out.println("After inner modification: " + unmodifiableNested);
    }
}

此代码演示了不可修改集合仅保护集合结构的一个级别。虽然我们无法从外部集合中添加或删除内部集合,但我们仍然可以修改内部集合本身。

输出结果显示,对内部集合的更改会反映在不可修改视图中。为了实现完全的不可变性,所有嵌套的集合也需要被设置为不可修改的。

性能注意事项

此示例检查了不可修改集合的性能特征。我们比较了对常规集合与其不可修改视图执行的操作。读取操作的开销很小。

PerformanceComparison.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class PerformanceComparison {

    public static void main(String[] args) {
        final int SIZE = 1_000_000;
        final int ITERATIONS = 100;
        
        Collection<Integer> largeList = new ArrayList<>();
        for (int i = 0; i < SIZE; i++) {
            largeList.add(i);
        }
        
        Collection<Integer> unmodifiable = 
            Collections.unmodifiableCollection(largeList);
        
        // Test iteration performance
        long start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            for (Integer num : largeList) {
                // Just iterate
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("Regular collection iteration: " + (end - start) + "ms");
        
        start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            for (Integer num : unmodifiable) {
                // Just iterate
            }
        }
        end = System.currentTimeMillis();
        System.out.println("Unmodifiable iteration: " + (end - start) + "ms");
        
        // Test contains performance
        start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            largeList.contains(SIZE / 2);
        }
        end = System.currentTimeMillis();
        System.out.println("Regular contains: " + (end - start) + "ms");
        
        start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            unmodifiable.contains(SIZE / 2);
        }
        end = System.currentTimeMillis();
        System.out.println("Unmodifiable contains: " + (end - start) + "ms");
    }
}

此性能测试表明,对不可修改集合的读取操作与常规集合相比,开销可以忽略不计。不可修改的包装器没有为迭代或包含检查等操作增加显著的性能成本。

输出结果表明,常规集合和不可修改集合之间的迭代和搜索时间几乎相同。不可修改的包装器是一个轻量级的视图,它不会复制数据。

来源

Java Collections.unmodifiableCollection 文档

在本文中,我们深入探讨了 Collections.unmodifiableCollection 方法。我们介绍了基本用法、不同的集合类型、性能特征和常见模式。理解不可修改的集合对于编写健壮的 Java 代码至关重要。

作者

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

列出所有Java教程