[英]Query on Collections.unmodifiableList() behaviour
我有一个关于Collections
unmodifiableList
API的查询。
程式码片段:
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());
}
}
}
我知道我无法修改无法修改的集合,因此我将收到错误消息
//list1.add("Flow");
我期望我将获得list
变量的只读列表,直到创建unmodifiableList
为止。 但即使在创建此list1
之后,父列表中的更改也会反映在子list1
我无法修改子级不可修改列表,但仍收到父级列表的修改。 对我来说是一个惊喜。
我知道该修复程序正在从父级创建新的Collection,但是Stil无法理解上述行为。
Collections
JavaDoc对此进行了清楚的解释:
返回指定列表的不可修改视图 。 此方法允许模块为用户提供对内部列表的“只读”访问权限。 对返回列表的查询操作“读取”到指定列表,并尝试直接或通过其迭代器修改返回列表,将导致UnsupportedOperationException。
(我大胆地提出了“看法”)
因此,当您这样做时:
List<Foo> unmodifiable = Collections.unmodifiableList(existingList);
...你不能调用修改列表unmodifiable.add(...)
等,但所造成的调用任何改变existingList.add(...)
将通过可见unmodifiable.get(...)
unmodifiable.size()
等
正如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));
}
}
现在再上一堂课:
contacts()
并获取他们可以浏览的列表 addContact()
将条目添加到列表中 list.add(new Contact(...))
使用addContact()
list.add(new Contact(...))
在多线程程序中,这可能很危险。 例如,如果在一个线程中,一个类使用不可修改的视图:
List<contacts> contactsView = contactManager.contacts();
int lastEntry = contactsView.size() - 1;
contactsView.get(lastEntry);
...在另一个线程中时,ContactManager修改列表:
contacts.remove(0);
...那么如果时间恰好如此,则第一个例程将获得IndexOutOfBoundsException
,因为列表在查询长度和尝试读取最后一个条目之间已经缩水。
如果要初始化列表,然后确保不对其进行进一步的更改,则可以通过限制指向可修改列表的变量范围来实现:
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);
}
responses
的范围就是这种方法。 该方法以外的任何内容都无法到达responses
,因此该方法以外的任何内容都无法修改列表。
如果您选择尽可能采用不可变对象的做法,那么通常这是一个好主意。
如果您有一个列表的内容将要更改,但是您希望呼叫者获得一个不变的列表,则需要制作该列表的防御性副本 。
public List<Contact> contacts() {
List<Contact> copy = new ArrayList<>();
List.copy(contacts, copy);
return Collections.unmodifiableMap(copy);
}
(在这种情况下,我们不需要使返回的列表不可修改来保护自己,但这对调用方很有帮助-如果调用方调用add()
或set()
它们将获得异常,而不是成功实际上并不会更新“真实”列表。
有一些库可以帮助解决这类问题,例如,Guava有一些更流畅的API,可以从头开始或作为现有列表的防御性副本来初始化不可变列表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.