java 如果我们有原始列表,为什么我们可以更改不可修改列表?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3650701/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 02:50:42  来源:igfitidea点击:

Why can we change the unmodifiable list if we have the original one?

javacollections

提问by Rakesh Juyal

By looking at the code of Collectionsclass, i got to know that when we are using the method unmodifiableList(List list)or unmodifiableCollection(Collection c)it is not creating a new object but it is returning the reference of the same object and overriding the methods which can modify the List[ add, addall, remove, retainAll... ]
So i ran this test:

通过查看Collections类的代码,我知道当我们使用该方法时,unmodifiableList(List list)或者unmodifiableCollection(Collection c)它不是创建新对象,而是返回同一对象的引用并覆盖可以修改List[ add, addall, remove, retainAll. .. ]
所以我运行了这个测试:

List modifiableList = new ArrayList();
modifiableList.add ( 1 );   
List unmodifiableList = Collections.unmodifiableList( modifiableList );
// unmodifiableList.add(3);  // it will throw the exception 
modifiableList.add ( 2 );       
System.out.println( unmodifiableList );

result is [ 1,2 ].
Now the point is why it is referring to the same object? Why it don't create a new object?

结果是[ 1,2 ]
现在的重点是为什么它指的是同一个对象?为什么它不创建一个新对象?

回答by Bozho

(answer of the queston at the bottom)

(底部问题的答案)

When you create an unmodifiable list, the purpose is that it should not be modified by people other than you- i.e. clients of an API.

当您创建一个不可修改的列表时,目的是它不应被除您以外的人修改- 即 API 的客户端。

the method unmodifiableList(..)creates a new object of type UnmodifiableList(but this is not a public class), which gets the original list, and delegates all methods to it exceptthe methods which would modify it.

该方法unmodifiableList(..)创建一个新的类型对象UnmodifiableList(但这不是公共类),它获取原始列表,并将所有方法委托给它,除了会修改它的方法。

The point is, as stated in the documentation:

关键是,如文档中所述:

Returns an unmodifiable view of the specified list. This method allows modules to provide users with "read-only" access to internal lists.

返回指定列表的不可修改视图。这种方法允许模块为用户提供对内部列表的“只读”访问。

So, an example: You have a Listof devices that your API has detected and can operate, and you want to give them a client of your API. But he is not supposed to changethem. So you have two options:

举个例子:你有一个List你的 API 已经检测到并且可以运行的设备,你想给他们一个你的 API 的客户端。但他不应该改变它们。所以你有两个选择:

  • give him a deep copy of your List, so that even if he modifies it, this does not change yourlist
  • give him an unmodifiable collection - he can't modify it, and you spare the creation of a new collection.
  • 给他一份你的深层副本List,这样即使他修改它,这也不会改变你的清单
  • 给他一个不可修改的集合——他不能修改它,你就不用创建一个新的集合了。

And now here comes the answer to the title of your question - the unmodifiable list is a viewof the original collection. So if you need to add a new item to it - say, you have discovered a new device that was just plugged-in, the clients will be able to see it in their unmodifiable view.

现在,您的问题标题有了答案——不可修改的列表是原始集合的视图。因此,如果您需要向其中添加新项目 - 例如,您发现了一个刚刚插入的新设备,客户将能够在其不可修改的视图中看到它。

回答by Raoul Duke

Now the point is why it is referring to the same object? Why it don't create a new object?

现在的重点是为什么它指的是同一个对象?为什么它不创建一个新对象?

Performance. It just doesn't scale to make a full copy. It would be a linear time operation to make a full copy which obviously isn't practical. Also, as others already noted, the point is that you can pass the reference of the unmodifiable list around without having to worry that it gets changed. This is very helpful for multithreaded programs.

表现。它只是无法扩展以制作完整副本。制作完整副本将是一个线性时间操作,这显然是不切实际的。此外,正如其他人已经指出的那样,重点是您可以传递不可修改列表的引用,而不必担心它会被更改。这对多线程程序非常有帮助。

回答by mik01aj

From documentation:

从文档:

public static List unmodifiableList(List list)

Returns an unmodifiable viewof the specified list. This method allows modules to provide users with "read-only" accessto internallists. 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.

public static List unmodifiableList(List list)

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

回答by Basil Bourque

The accepted Answerby Bozhois correct. Here's a bit more info, example code, and a suggested alternative.

接受的答案Bozho是正确的。这里有更多信息、示例代码和建议的替代方案。

The unmodifiableList Is Backed By Original List

unmodifiableList 由原始列表支持

That unmodifiableListmethod in Collectionsutility class does not create a new list, it creates a pseudo-list backedby 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.

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

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.

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

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

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

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

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

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

Collections.unmodifiableMapCollections.unmodifiableSet、 等同上。

Here is another example demonstrating the issue.

这是另一个演示该问题的示例。

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]

Google Guava

谷歌番石榴

Instead of the Collectionsclass, for defensive programming I recommend using the Google Guavalibrary and its ImmutableCollectionsfacility.

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

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 notaffect (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.

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

回答by Kumar Abhishek

I Found one way to do this is

我发现一种方法是

List unmodifiableList = Collections.unmodifiableList( new ArrayList(modifiableList));

List<String> strings = new ArrayList<String>();
        // unmodifiable.add("New string");
        strings.add("Aha 1");
        strings.add("Aha 2");
        List<String> unmodifiable = Collections.unmodifiableList(strings);
        List<String> immutableList = Collections.unmodifiableList(new ArrayList<>(strings));
        // Need some way to fix it so that Strings does not Modify
        strings.add("Aha 3");
        strings.add("Aha 4");

        strings.remove(0);

        for (String str : unmodifiable) {
            System.out.println("Reference Modified :::" + str);
        }

        for (String str : immutableList) {
            System.out.println("Reference Modified :::" + str);
        }

回答by Pawe? Dyda

I believe the secret lies in implementation details... Collection.unmodifiableList() will simply give you decorated modifiable list. I mean unmodifiable list contains reference to modifiable list internally.

我相信秘密在于实现细节...... Collection.unmodifiableList() 只会给你修饰的可修改列表。我的意思是不可修改列表包含对内部可修改列表的引用。

回答by Dead Programmer

  1. you should go for creating new Object of a list, only when the original Object is going to be changed and you need a backup , when someone corrupts it , u can replace by new Object.

  2. To create a ummodifiable object, i will wrap the original object and prevent add ,remove by throwing exception. but u know ,i can change each object present in the list .like if u have a person object in an umodifiable list , i can still change the name of the person object in the list.

  1. 您应该去创建列表的新对象,只有当原始对象将要更改并且您需要备份时,当有人损坏它时,您可以用新对象替换。

  2. 要创建一个 ummodifiable 对象,我将包装原始对象并通过抛出异常来防止添加和删除。但是您知道,我可以更改列表中存在的每个对象。就像如果您在可修改列表中有一个 person 对象,我仍然可以更改列表中 person 对象的名称。