请考虑以下代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Integer> intList1=new ArrayList<Integer>();
List<Integer> intList2;
intList1.add(1);
intList1.add(2);
intList1.add(3);
intList2=Collections.unmodifiableList(intList1);
intList1.add(4);
for(int i=0;i<4;i++)
{
System.out.println(intList2.get(i));
}
}
}
上述代码的结果是
1
2
3
4
在上面的代码中,我们从List intList2
1的内容创建了一个不可修改的 List intList
。但在Collections.unmodifiable
声明后,当我对intList1
进行更改时,该更改会反映为intList2
。这怎么可能?
答案 0 :(得分:3)
您需要阅读Javadoc for Collections.unmodifiableList
返回指定列表的不可修改视图。
这意味着返回的视图是不可修改的。如果您有原始参考,则可以更改集合。如果更改了集合,则更改将反映在视图中。
这样做的好处是速度非常快,即您不需要复制集合,但是您注意到的缺点 - 生成的集合是一个视图。
为了创建真正不可修改的集合,您需要复制然后换行:
intList2=Collections.unmodifiableList(new ArrayList<>(intList1));
这会将intList1
的内容复制到另一个集合中,然后将该集合包装在不可修改的变量中。不存在对包装集合的引用。
这是昂贵的 - 整个底层数据存储区(在这种情况下是一个数组)需要重复,通常需要O(n)
。
Google Guava提供immutable collections解决了制作防御性副本的一些问题:
但是,当使用集合的不可变副本而不是不可修改的视图时,速度仍然是关键问题。
应该注意Collections.unmodifiableXXX
的通常用法是从方法返回,例如getter:
public Collection<Thing> getThings() {
return Collections.unmodifiableCollection(things);
}
在这种情况下,有两点需要注意:
getThings
的用户无法访问things
,因此无法修改无法修改。things
都会非常昂贵。总之,您的问题的答案比您预期的要复杂得多,在您的应用程序中传递集合时需要考虑许多方面。
答案 1 :(得分:1)
来自Collections.unmodifiableList的Javadoc:
返回指定列表的不可修改视图。此方法允许模块为用户提供对内部列表的“只读”访问。
它阻止修改返回的列表,但原始列表本身仍然可以。
答案 2 :(得分:1)
Collections.unmodifiableList返回内部列表的“只读”视图。虽然返回的对象不可修改,但可以修改它引用的原始列表。两个对象都指向内存中的同一个对象,因此它将反映所做的更改。
Here是对正在发生的事情的一个很好的解释。
答案 3 :(得分:0)
发生这种情况是因为不可修改的列表在内部支持第一个列表,如果您真的希望它不可修改,则不应再使用第一个列表。
答案 4 :(得分:0)
在您的代码中
intList2=Collections.unmodifiableList(intList1);
在intList2中创建unmodifiableList。因此您可以自由地在inList1中进行更改 但是你不允许在intList2中做任何改变
试试这个: intList2.add(4); 你会得到
java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.add(Unknown Source)
以上异常。