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

发布于 2024-09-18 00:55:48 字数 805 浏览 5 评论 0 原文

通过查看 Collections< 的代码/code> 类,我知道当我们使用方法 unmodifyingList(List list)unmodifyingCollection(Collection c) 时,它不会创建一个新对象,但它返回同一对象的引用并重写可以修改 List 的方法 [ add, addall, 删除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 );

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

By looking at the code of Collections class, 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:

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

痴意少年 2024-09-25 00:55:48

(底部问题的答案)

当您创建不可修改的列表时,目的是它不应该被除您之外的其他人(即 API 的客户端)修改。

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

正如文档中所述,重点是:

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

举个例子:您有一个 API 已检测到并可以操作的设备列表,并且您希望为它们提供 API 的客户端。但他不应该改变它们。所以你有两个选择:

  • 给他一个你的List的深层副本,这样即使他修改它,这也不会改变你的列表
  • 给他一个不可修改的集合 - 他无法修改它,并且您无需创建新集合。

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

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

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 except the methods which would modify it.

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 List of 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 change them. So you have two options:

  • give him a deep copy of your List, so that even if he modifies it, this does not change your list
  • give him an unmodifiable collection - he can't modify it, and you spare the creation of a new collection.

And now here comes the answer to the title of your question - the unmodifiable list is a view of 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.

胡大本事 2024-09-25 00:55:48

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

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

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.

梅倚清风 2024-09-25 00:55:48

来自文档:

public static List unmodifyingList(List list)

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

From documentation:

public static List unmodifiableList(List list)

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.

等你爱我 2024-09-25 00:55:48

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

unmodifierList 由原始列表支持

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

类文档中对此进行了详细说明:

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

第四个词是关键:view。新列表对象不是新列表。它是一个覆盖层。就像描图纸绘图上的透明胶片 会阻止您在绘图上做标记,但不会阻止您进入绘图下方来修改原图。

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

同上 <代码>Collections.unmodifyingMapCollections.unmodifyingSet,等等。

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

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

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

您可以制作一份新的清单。

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

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

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

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

The accepted Answer by Bozho is correct. Here's a bit more info, example code, and a suggested alternative.

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.

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

墨小墨 2024-09-25 00:55:48

我发现一种方法是

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);
        }

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);
        }
百变从容 2024-09-25 00:55:48

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

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.

瞎闹 2024-09-25 00:55:48

查看 UnmodifyingCollection 代码会有所帮助。我添加了部分代码并添加了一些注释。您可以看到没有创建新集合,

请查看 See! 部分

static class UnmodifiableCollection<E> implements Collection<E>, Serializable {

    //See! this keeps a reference to backed collection
    final Collection<? extends E> c; 

    UnmodifiableCollection(Collection<? extends E> c) {
        if (c==null)
            throw new NullPointerException();
        //See! no new collection is created. 
        //this is why The unmodifiableList is Backed By Original Collection
        this.c = c; 
    }

    //See! All modification operations are overridden to just do noting 
    public boolean add(E e) {
        throw new UnsupportedOperationException();
    }
    //See! do nothing again!
    public boolean remove(Object o) {
        throw new UnsupportedOperationException();
    }
    //See! do nothing again!
    public boolean containsAll(Collection<?> coll) {
        return c.containsAll(coll);
    }
    //See! do nothing again!
    public boolean addAll(Collection<? extends E> coll) {
        throw new UnsupportedOperationException();
    }
    //See! All non modification just call Backed collection methods
    public int size()                          {return c.size();}
    public boolean isEmpty()                   {return c.isEmpty();}
    public boolean contains(Object o)          {return c.contains(o);}

Having a look at UnmodifiableCollection code can help. I have added some portion of the code and added some comments. You can see that no new collection is created,

Have a look at See! sections

static class UnmodifiableCollection<E> implements Collection<E>, Serializable {

    //See! this keeps a reference to backed collection
    final Collection<? extends E> c; 

    UnmodifiableCollection(Collection<? extends E> c) {
        if (c==null)
            throw new NullPointerException();
        //See! no new collection is created. 
        //this is why The unmodifiableList is Backed By Original Collection
        this.c = c; 
    }

    //See! All modification operations are overridden to just do noting 
    public boolean add(E e) {
        throw new UnsupportedOperationException();
    }
    //See! do nothing again!
    public boolean remove(Object o) {
        throw new UnsupportedOperationException();
    }
    //See! do nothing again!
    public boolean containsAll(Collection<?> coll) {
        return c.containsAll(coll);
    }
    //See! do nothing again!
    public boolean addAll(Collection<? extends E> coll) {
        throw new UnsupportedOperationException();
    }
    //See! All non modification just call Backed collection methods
    public int size()                          {return c.size();}
    public boolean isEmpty()                   {return c.isEmpty();}
    public boolean contains(Object o)          {return c.contains(o);}
凉宸 2024-09-25 00:55:48
  1. 你应该去创建一个列表的新对象,只有当原始对象要改变并且你需要备份时,当有人破坏它时,你可以用新对象替换。

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

  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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文