为什么我的`unmodifiableList`可以修改? [英] Why is my `unmodifiableList` modifiable?

查看:995
本文介绍了为什么我的`unmodifiableList`可以修改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个列表,其元素不能删除也不能添加。我以为我会找到答案与 Collections.unmodifiableList 。我通过我的原始列表,并得到一个假定不可修改的列表。



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



查看此演示代码。

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

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

List< String> unmodList = Collections.unmodifiableList(originalList);
System.out.println(unmod before:+ unmodList); // Yields [dog,cat,bird]
originalList.remove(cat); //从原始列表中删除元素会影响不可修改的列表?
System.out.println(unmod after:+ unmodList); // yields [dog,bird]


解决方案

unmodifiableList由原始列表



unmodifiableList 方法 集合 实用程序类不创建新列表,它会创建一个伪列表。通过不可修改对象进行的任何添加或删除尝试都将被阻止,因此该名称符合其目的。但实际上,如你所示,原始列表可以修改,同时影响我们的次要不完全不可修改的列表。



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


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


第四个字是键: view 。新的列表对象不是一个新的列表。它是一个叠加。与跟踪纸透明胶片在绘图上阻止您在绘图上制作标记,它不会阻止您从底下修改原始绘图。



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



同上 Collections.unmodifiableMap Collections.unmodifiableSet ,等等。



Google Guava



而不是 Collections 建议使用 Google Guava 媒体库及其 ImmutableCollections 设施。



您可以创建一个新的列表。

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

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

  ImmutableList< String> ANIMALS = ImmutableList.copyOf(originalList); //防御复制!但是请记住,虽然集合自身的定义是分开的,但是包含的对象由原始列表共享和新的不可变列表。当做防守副本时,我们不会复制狗对象。只有一个狗对象保留在内存中,两个列表都包含指向同一个狗的引用。如果dog对象中的属性被修改,两个集合都指向同一个单独的狗对象,因此这两个集合将看到狗的新鲜属性值。


I want a List whose elements cannot be removed nor added. I thought I'd found the answer with Collections.unmodifiableList in Java 8. I pass my original list and get back a supposedly unmodifiable list.

Yet when I delete an element from the original list, my unmodifiable list is modified. What is going on?

See this demo code. My unmodifiable list shrinks from 3 elements 2 when deleting from original.

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]

解决方案

The unmodifiableList Is Backed By Original List

That unmodifiableList method in Collections utility class does not create a new list, it creates a pseudo-list backed by the original list. Any add or remove attempts made through the "unmodifiable" object will be blocked, thus the name lives up to its purpose. But indeed, as you have shown, the original list can be modified and simultaneously affects our secondary not-quite-unmodifiable list.

This is spelled out in the class documentation:

Returns an unmodifiable view of the specified list. This method allows modules to provide users with "read-only" access to internal lists. Query operations on the returned list "read through" to the specified list, and attempts to modify the returned list, whether direct or via its iterator, result in an UnsupportedOperationException.

That fourth word is key: view. The new list object is not a fresh list. It is an overlay. Just like tracing paper or transparency film over a drawing stops you from making marks on the drawing, it does not stop you from going underneath to modify the original drawing.

Moral of the Story: Do not use Collections.unmodifiableList for making defensive copies of lists.

Ditto for Collections.unmodifiableMap, Collections.unmodifiableSet, and so on.

Google Guava

Instead of the Collections class, for defensive programming I recommend using the Google Guava library and its ImmutableCollections facility.

You can make a fresh list.

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

Or you can make a defensive copy of an existing list. In this case you will get a fresh separate list. Deleting from the original list will not affect (shrink) the immutable list.

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

But remember, while the collection’s own definition is separate, the contained objects are shared by both the original list and new immutable list. When making that defensive copy, we are not duplicating the "dog" object. Only one dog object remains in memory, both lists contain a reference pointing to the same dog. If the properties in the "dog" object are modified, both collections are pointing to that same single dog object and so both collections will see the dog’s fresh property value.

这篇关于为什么我的`unmodifiableList`可以修改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆