简体   繁体   English

查询Collections.unmodifiableList()行为

[英]Query on Collections.unmodifiableList() behaviour

I have a query regarding unmodifiableList API of Collections . 我有一个关于Collections unmodifiableList API的查询。

Code snippet: 程式码片段:

import java.util.*;

public class ReadOnlyList{
    public static void main(String args[]){
        List<String> list = new ArrayList<String>();
        list.add("Stack");
        list.add("Over");
        List list1 = Collections.unmodifiableList(list);
        //list1.add("Flow");
        list.add("Flow");
        System.out.println("sizes:1:2:"+list.size()+":"+list1.size());
        for (Iterator it = list1.iterator();it.hasNext();){
            System.out.println("elements:"+it.next());
        }
        System.out.println("__________");
        list.remove("Flow");
        for (Iterator it = list1.iterator();it.hasNext();){
            System.out.println("elements:"+it.next());
        }       
    }
}

I know that I can't modify the unmodifible collection and hence I will get error for 我知道我无法修改无法修改的集合,因此我将收到错误消息

//list1.add("Flow");

I expected that I will get read-only list of list varaible until the point I create unmodifiableList . 我期望我将获得list变量的只读列表,直到创建unmodifiableList为止。 But even after this list1 creation, the changes in parent list are reflected in child list1 但即使在创建此list1之后,父列表中的更改也会反映在子list1

I can't modify child unmodifible list but still I receive modifications of parent list. 我无法修改子级不可修改列表,但仍收到父级列表的修改。 It's a surprise for me. 对我来说是一个惊喜。

I know that fix is creating new Collection from parent but stil did not understand above behaviour. 我知道该修复程序正在从父级创建新的Collection,但是Stil无法理解上述行为。

The Collections JavaDoc explains this clearly: Collections JavaDoc对此进行了清楚的解释:

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。

(I emboldened "view") (我大胆地提出了“看法”)

So when you do: 因此,当您这样做时:

 List<Foo> unmodifiable = Collections.unmodifiableList(existingList);

... you cannot modify the list by calling unmodifiable.add(...) etc. But any changes caused by calls to existingList.add(...) will be visible through unmodifiable.get(...) , unmodifiable.size() etc. ...你不能调用修改列表unmodifiable.add(...)等,但所造成的调用任何改变existingList.add(...)将通过可见unmodifiable.get(...) unmodifiable.size()

As the Javadoc says, this means you can give another object a read-only view of a list that your class will continue to modify: 正如Javadoc所说,这意味着您可以为另一个对象提供一个列表的只读视图,您的类将继续对其进行修改:

class ContactManager {

   List<Contact> contacts = new ArrayList<>();        

    public List<Contact> contacts() {
        return Collection.unmodifiableList(contacts);
    }

    public void addContact(String name) {
       contacts.add(new Contact(name));
    }
}

Now another class: 现在再上一堂课:

  • can call contacts() and get a List they can browse 可以调用contacts()并获取他们可以浏览的列表
  • can call addContact() to add an entry to the list 可以调用addContact()将条目添加到列表中
  • can see the new contact in their view of the list 可以在列表视图中看到新联系人
  • cannot avoid using addContact() by instead calling list.add(new Contact(...)) 无法避免通过调用list.add(new Contact(...))使用addContact() list.add(new Contact(...))

This could be dangerous in a multi-threaded program. 在多线程程序中,这可能很危险。 For example, if in one thread, a class works with the unmodifiable view: 例如,如果在一个线程中,一个类使用不可修改的视图:

 List<contacts> contactsView = contactManager.contacts();
 int lastEntry = contactsView.size() - 1;
 contactsView.get(lastEntry);

... while in another thread, the ContactManager modifies the list: ...在另一个线程中时,ContactManager修改列表:

 contacts.remove(0);

... then if the timing is just-so, the first routine will get an IndexOutOfBoundsException , because the list has shrunk between querying the length and trying to read the last entry. ...那么如果时间恰好如此,则第一个例程将获得IndexOutOfBoundsException ,因为列表在查询长度和尝试读取最后一个条目之间已经缩水。


If you want to initialise a list then ensure you don't make further changes to it, you can achieve that by restricting the scope of the variable pointing to the modifiable list: 如果要初始化列表,然后确保不对其进行进一步的更改,则可以通过限制指向可修改列表的变量范围来实现:

 private List<String> validResponses() {
       List<String> responses = new ArrayList<>();
       values.add("Yes");
       values.add("Agree")
       values.add("No");
       values.add("Disagree");
       return Collections.unmodifiableList(responses);
 }

The scope of responses is just this method. responses的范围就是这种方法。 Nothing outside the method can reach responses , and so nothing outside this method can modify the list. 该方法以外的任何内容都无法到达responses ,因此该方法以外的任何内容都无法修改列表。

This is generally a good idea, if you choose to adopt the practice of using immutable objects wherever possible. 如果您选择尽可能采用不可变对象的做法,那么通常这是一个好主意。


If you have a list whose contents are going to be changing, but you want callers to get an unchanging list, you need to make a defensive copy of the list. 如果您有一个列表的内容将要更改,但是您希望呼叫者获得一个不变的列表,则需要制作该列表的防御性副本

public List<Contact> contacts() {
     List<Contact> copy = new ArrayList<>();
     List.copy(contacts, copy);
     return Collections.unmodifiableMap(copy);
}

(In this case, we didn't need to make the returned list unmodifiable to protect ourselves, but it's helpful to the caller -- if they call add() or set() they'll get an exception, instead of a success that doesn't actually update the "real" list. (在这种情况下,我们不需要使返回的列表不可修改来保护自己,但这对调用方很有帮助-如果调用方调用add()set()它们将获得异常,而不是成功实际上并不会更新“真实”列表。


There are libraries that can help with this kind of thing, for example Guava has some more fluent APIs for initialising immutable lists, either from scratch or as defensive copies of existing lists. 有一些库可以帮助解决这类问题,例如,Guava有一些更流畅的API,可以从头开始或作为现有列表的防御性副本来初始化不可变列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM