7

我想要一个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]
4

1 回答 1

8

unmodifiableList 由原始列表支持

实用程序类中的该unmodifiableList方法Collections不会创建新列表,它会创建一个由原始列表支持的伪列表。通过“不可修改”对象进行的任何添加或删除尝试都将被阻止,因此该名称符合其目的。但事实上,正如您所展示的,原始列表可以修改,同时会影响我们的第二个非完全不可修改的列表。

这在类文档中有详细说明:

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

第四个字是关键:view。新列表对象不是新列表。这是一个覆盖。就像绘图上的描图纸透明胶片阻止您在绘图上做标记一样,它不会阻止您深入到下面修改原始绘图。

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

Collections.unmodifiableMap,等也是如此Collections.unmodifiableSet

谷歌番石榴

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

你可以做一个新的清单。

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

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

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

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

于 2015-09-02T20:41:59.147 回答