通过查看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 );
结果为[ 1,2 ]
现在重点是为什么它指的是同一个对象?为什么不创建新对象?
答案 0 :(得分:11)
(底部的问题答案)
当您创建不可修改的列表时,其目的是不应由以外的人修改 - 即API的客户端。
方法unmodifiableList(..)
创建一个类型为UnmodifiableList
的新对象(但这不是公共类),它获取原始列表,并将所有方法委托给它除了修改它的方法。
关键是,正如文件中所述:
返回指定列表的不可修改视图。此方法允许模块为用户提供对内部列表的“只读”访问。
所以,举个例子:您有一个List
个设备已经检测到并且可以运行,并且您希望为他们提供API的客户端。但他不应该改变他们。所以你有两个选择:
List
的深层副本,这样即使他修改了它,也不会改变你的列表现在,问题的标题就是答案 - 不可修改的列表是原始集合的视图。因此,如果您需要向其中添加新项目 - 比如说,您发现了一个刚刚插入的新设备,客户端将能够在不可修改的视图中看到它。
答案 1 :(得分:3)
现在重点是它所指的原因 对同一个对象?为什么不呢 创建一个新对象?
性能。它只是不缩放以制作完整副本。制作完整副本将是一个线性时间操作,这显然是不切实际的。此外,正如其他人已经指出的那样,关键是您可以传递不可修改列表的引用,而不必担心它会被更改。这对多线程程序非常有用。
答案 2 :(得分:2)
来自文档:
public static List unmodifiableList(List list)
返回指定列表的不可修改的视图。此方法允许模块为用户提供“只读”访问到内部列表。对返回列表的查询操作“读取”到指定列表,并尝试修改返回的列表,无论是直接还是通过其迭代器,都会导致UnsupportedOperationException。
答案 3 :(得分:0)
我相信秘密在于实现细节...... Collection.unmodifiableList()将简单地为您提供可修改的可修改列表。我的意思是不可修改的列表包含对内部可修改列表的引用。
答案 4 :(得分:0)
unmodifiableList
实用程序类中的Collections
方法不会创建新列表,而是由原始列表创建伪列表后备。通过“不可修改”对象进行的任何添加或删除尝试都将被阻止,因此名称符合其目的。但实际上,正如您所示,原始列表可以被修改,同时影响我们的次要非完全不可修改的列表。
这在课程文档中有详细说明:
返回指定列表的不可修改视图。此方法允许模块为用户提供对内部列表的“只读”访问。对返回列表的查询操作“读取”到指定列表,并尝试修改返回的列表,无论是直接还是通过其迭代器,都会导致UnsupportedOperationException。
第四个词是关键:view
。新列表对象不是新列表。这是一个叠加。就像绘图上的tracing paper或transparency film阻止您在图纸上制作标记一样,它也不会阻止您在下方修改原始图形。
故事的道德:不要使用Collections.unmodifiableList来制作列表的防御性副本。
同样适用于Collections.unmodifiableMap
,Collections.unmodifiableSet
,等等。
这是另一个证明问题的例子。
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]
对于防御性编程而不是Collections
类,我建议使用Google Guava库及其ImmutableCollections工具。
您可以制作新的清单。
public static final ImmutableList<String> ANIMALS = ImmutableList.of(
dog,
cat,
bird );
或者您可以制作现有列表的防御副本。在这种情况下,您将获得一个新的单独列表。从原始列表中删除不影响(缩小)不可变列表。
ImmutableList<String> ANIMALS = ImmutableList.copyOf( originalList ); // defensive copy!
但请记住,虽然集合自己的定义是独立的,但是包含的对象由原始列表和新的不可变列表共享。在制作防御性副本时,我们不会复制“狗”对象。只有一个狗对象保留在内存中,两个列表都包含指向同一只狗的引用。如果修改了“dog”对象中的属性,则两个集合都指向同一个dog对象,因此两个集合都将看到dog的新属性值。
答案 5 :(得分:0)
我发现一种方法是
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);
}
答案 6 :(得分:-2)
你应该去创建列表的新对象,只有当原始对象要改变并且你需要备份时,当有人破坏它时,你可以用新的对象替换。
要创建一个ummodifiable对象,我将包装原始对象并阻止添加,通过抛出异常删除。但是你知道,我可以改变列表中的每个对象。就像你在umodifiable列表中有一个person对象一样,我仍然可以在列表中更改person对象的名称。