Java 为什么不';使用Collections类中不可修改的方法,使用新元素创建集合?

Java 为什么不';使用Collections类中不可修改的方法,使用新元素创建集合?,java,list,collections,unmodifiable,Java,List,Collections,Unmodifiable,假设有以下代码: List<String> modifiableList = new ArrayList<String>(Arrays.asList("1","2","3")); List<String> unmodifiableList = Collections.unmodifiableList(modifiableList); System.out.println(unmodifiableList); modifiableList.remove("1")

假设有以下代码:

List<String> modifiableList = new ArrayList<String>(Arrays.asList("1","2","3"));
List<String> unmodifiableList = Collections.unmodifiableList(modifiableList);
System.out.println(unmodifiableList);
modifiableList.remove("1");
modifiableList.remove("3");
System.out.println(unmodifiableList);
如果将第二行更改为

List<String> unmodifiableList = Collections.unmodifiableList(
                                       new ArrayList<String>(modifiableList));
List unmodifiableList=Collections.unmodifiableList(
新建ArrayList(可修改列表));
它按预期工作。 问题是为什么集合中的
UnmodifiableList
内部类(以及所有其他不可修改的类)不从那里创建原始列表的新副本,例如
ArrayList
的构造函数为什么不创建原始列表的新副本,以便真正使其不可修改


编辑:我理解代码的作用;我的问题是为什么要这样实施?为什么来自UnmodifiableList(集合中的内部类)的构造函数在创建基础数组的新副本时表现得像ArrayList的构造函数?为什么可修改的集合(ArrayList)复制整个内容,而不可修改的集合不复制

原因很简单,就是效率。复制一个集合的所有元素可能非常耗时,特别是如果正在包装的集合具有某种魔力,比如JPA延迟加载,并且需要额外的内存。按原样包装基础集合是一个不需要额外开销的简单操作。如果开发人员确实想要一个单独的副本(不可修改或不可修改),那么显式地创建它是非常容易的。(我倾向于使用番石榴
不可变*

原因很简单。复制一个集合的所有元素可能非常耗时,特别是如果正在包装的集合具有某种魔力,比如JPA延迟加载,并且需要额外的内存。按原样包装基础集合是一个不需要额外开销的简单操作。如果开发人员确实想要一个单独的副本(不可修改或不可修改),那么显式地创建它是非常容易的。(我倾向于使用番石榴
不可变*

这些方法的目的是在现有集合上创建一个不可修改的视图。这就是记录在案的行为,在许多情况下,这正是您想要的-它比复制所有数据更有效,例如。。。或者,您希望将反映您想要进行的任何更改的调用方集合交给他们,但不允许他们进行更改

如果您想要数据的不可变副本(或至少是引用…),那么只需创建一个副本,然后在其顶部创建一个不可变视图,就像您现在这样

因此,基本上,您可以基于
集合轻松创建视图或副本和视图。不可修改*
本身执行视图操作。我们有两个正交运算:

  • 创建副本(例如,通过构造函数)
  • 创建视图(通过集合。不可修改*)
这些操作可以很容易地组合。如果
Collections.unmodifiable*
实际执行了“仅复制”,那么我们需要其他操作才能创建视图。如果您认为这两个选项在不同的情况下都很有用,那么将它们组合起来会带来很大的灵活性。

这些方法的目的是在现有集合上创建一个不可修改的视图。这就是记录在案的行为,在许多情况下,这正是您想要的-它比复制所有数据更有效,例如。。。或者,您希望将反映您想要进行的任何更改的调用方集合交给他们,但不允许他们进行更改

如果您想要数据的不可变副本(或至少是引用…),那么只需创建一个副本,然后在其顶部创建一个不可变视图,就像您现在这样

因此,基本上,您可以基于
集合轻松创建视图或副本和视图。不可修改*
本身执行视图操作。我们有两个正交运算:

  • 创建副本(例如,通过构造函数)
  • 创建视图(通过集合。不可修改*)

这些操作可以很容易地组合。如果
Collections.unmodifiable*
实际执行了“仅复制”,那么我们需要其他操作才能创建视图。如果您承认这两个选项在不同的情况下都很有用,那么将它们组合起来会带来很大的灵活性。

请注意,unmodifiableList会返回所提供列表的“unmodifiable视图”。因此列表本身保持不变(仍然可以修改),只有其“不可修改视图”是不可修改的。您可以将其视为SQL表和视图——您可以在表上运行DML脚本,它将反映在相关视图上。至于ArrayList,它有。。。一个数组,它的实现特性是从提供的源列表中复制元素(实际上不必由数组支持)。它回答了您的问题吗?

请注意,unmodifiableList返回所提供列表的“unmodifiable视图”。因此列表本身保持不变(仍然可以修改),只有其“不可修改视图”是不可修改的。您可以将其视为SQL表和视图——您可以在表上运行DML脚本,它将反映在相关视图上。至于ArrayList,它有。。。一个数组,它的实现特性是从提供的源列表中复制元素(实际上不必由数组支持)。它回答了你的问题吗?

@ᴍ阿伦ᴍ我理解代码的作用;请参见编辑@ᴍ阿伦ᴍ我理解代码的作用;请参阅编辑。实际上复制元素并不那么耗时,因为它可以使用
数组。copy()
使用类似
memcpy()
的东西,即O(1)而不是O(n)。@m3th0dman在一般情况下不是。这可能特别适用于
ArrayList
,但是很多
List
impl
List<String> unmodifiableList = Collections.unmodifiableList(
                                       new ArrayList<String>(modifiableList));