为什么我的`unmodifiableList`可以修改?

时间:2015-09-02 20:41:59

标签: java list collections unmodifiable

我想要一个List,其元素无法删除也无法添加。我以为我在Java 8中找到了Collections.unmodifiableList的答案。我传递了我的原始列表并找回了一个据称无法修改的列表。

然而,当我从原始列表中删除元素时,我的不可修改列表被修改。到底是怎么回事?

请参阅此演示代码。从原始文件中删除时,我的不可修改列表从3个元素2缩小。

String dog = "dog";
String cat = "cat";
String bird = "bird";

List< String > originalList = new ArrayList<>( 3 );
originalList.add( dog );
originalList.add( cat );
originalList.add( bird );

List< String > unmodList = Collections.unmodifiableList( originalList );
System.out.println( "unmod before: " + unmodList );  // Yields [dog, cat, bird]
originalList.remove( cat );  // Removing element from original list affects the unmodifiable list?
System.out.println( "unmod after: " + unmodList );  // Yields [dog, bird]

1 个答案:

答案 0 :(得分:8)

unmodifiableList由原始列表支持

unmodifiableList实用程序类中的Collections方法不会创建新列表,而是由原始列表创建伪列表后备。通过&#34;不可修改的&#34;进行的任何添加或删除尝试对象将被阻止,因此名称符合其目的。但实际上,正如您所示,原始列表可以被修改,同时影响我们的次要非完全不可修改的列表。

这在课程文档中有详细说明:

  

返回指定列表的不可修改视图。该方法允许模块为用户提供&#34;只读&#34;访问内部列表。对返回列表的查询操作&#34;通读&#34;到指定的列表,并尝试修改返回的列表,无论是直接还是通过其迭代器,都会导致UnsupportedOperationException。

第四个词是关键:view。新列表对象不是新列表。这是一个叠加。就像绘图上的tracing papertransparency film阻止您在图纸上制作标记一样,它也不会阻止您在下方修改原始图形。

故事的道德:不要使用Collections.unmodifiableList来制作列表的防御性副本。

同样适用于Collections.unmodifiableMapCollections.unmodifiableSet,等等。

Google Guava

对于防御性编程而不是Collections类,我建议使用Google Guava库及其ImmutableCollections工具。

您可以制作新的清单。

public static final ImmutableList<String> ANIMALS = ImmutableList.of(
        dog,
        cat,
        bird );

或者您可以制作现有列表的防御副本。在这种情况下,您将获得一个新的单独列表。从原始列表中删除影响(缩小)不可变列表。

ImmutableList<String> ANIMALS = ImmutableList.copyOf( originalList ); // defensive copy!

但请记住,虽然集合自己的定义是独立的,但是包含的对象由原始列表和新的不可变列表共享。在制作防御性副本时,我们不会复制“狗”#34;宾语。只有一个狗对象保留在内存中,两个列表都包含指向同一只狗的引用。如果&#34; dog&#34;中的属性对象被修改,两个集合都指向同一个单一的狗对象,因此两个集合都将看到狗的新属性值。