Answers:
不可修改的集合通常是可修改集合的包装,其他代码仍可以访问该可修改集合。所以,虽然你不能做任何更改,如果你只需要不可修改的集合的引用,你不能依靠内容不改变。
一个不可变的集合保证没有什么可以改变集合了。如果包装了可修改的集合,则确保没有其他代码可以访问该可修改的集合。请注意,尽管没有代码可以更改集合包含引用的对象,但是对象本身仍可能是可变的-创建一个不可变的集合StringBuilder
不会以某种方式“冻结”这些对象。
基本上,区别在于其他代码是否可以更改背后的集合。
list
。如果以后可以调用某些东西,list.add(10)
那么coll
它将反映出这种变化,所以不,我不会将其称为不变的。
基本上unModifiable
Collection是一个视图,因此可以间接地从其他可修改的引用中对其进行“修改”。同样,因为它只是另一个集合的只读视图,所以当源集合发生更改时,不可修改的集合将始终显示最新值。
但是,immutable
可以将Collection视为另一个Collection的只读副本,并且不能进行修改。在这种情况下,当源集合更改时,不可变的集合不会反映更改
这是一个可视化此差异的测试用例。
@Test
public void testList() {
List<String> modifiableList = new ArrayList<String>();
modifiableList.add("a");
System.out.println("modifiableList:"+modifiableList);
System.out.println("--");
//unModifiableList
assertEquals(1, modifiableList.size());
List<String> unModifiableList=Collections.unmodifiableList(
modifiableList);
modifiableList.add("b");
boolean exceptionThrown=false;
try {
unModifiableList.add("b");
fail("add supported for unModifiableList!!");
} catch (UnsupportedOperationException e) {
exceptionThrown=true;
System.out.println("unModifiableList.add() not supported");
}
assertTrue(exceptionThrown);
System.out.println("modifiableList:"+modifiableList);
System.out.println("unModifiableList:"+unModifiableList);
assertEquals(2, modifiableList.size());
assertEquals(2, unModifiableList.size());
System.out.println("--");
//immutableList
List<String> immutableList=Collections.unmodifiableList(
new ArrayList<String>(modifiableList));
modifiableList.add("c");
exceptionThrown=false;
try {
immutableList.add("c");
fail("add supported for immutableList!!");
} catch (UnsupportedOperationException e) {
exceptionThrown=true;
System.out.println("immutableList.add() not supported");
}
assertTrue(exceptionThrown);
System.out.println("modifiableList:"+modifiableList);
System.out.println("unModifiableList:"+unModifiableList);
System.out.println("immutableList:"+immutableList);
System.out.println("--");
assertEquals(3, modifiableList.size());
assertEquals(3, unModifiableList.size());
assertEquals(2, immutableList.size());
}
输出量
modifiableList:[a]
--
unModifiableList.add() not supported
modifiableList:[a, b]
unModifiableList:[a, b]
--
immutableList.add() not supported
modifiableList:[a, b, c]
unModifiableList:[a, b, c]
immutableList:[a, b]
--
modifiableList
和unModifiableList
增加的immutableList
大小均未更改
new ArrayList<String>(modifiableList)
immutableList的引用不能被修改
new ArrayList<String>(modifiableList)
因为没有引用new
?谢谢。
我认为主要区别在于,可变集合的所有者可能希望提供对其他代码的访问,但通过不允许其他代码修改集合的接口(保留该功能)来提供访问权限到拥有的代码)。因此,集合不是一成不变的,但是不允许某些用户更改集合。
Oracle的Java Collection Wrapper教程说了这句话(添加了重点):
不可修改的包装器有两个主要用途,如下所示:
- 使集合一旦建立便不可变。在这种情况下,最好不要保留对后备集合的引用。这绝对保证了不变性。
- 允许某些客户端以只读方式访问您的数据结构。您保留对后备集合的引用,但分发对包装器的引用。这样,在您保持完全访问权限的同时,客户端可以查看但不能修改。
如果我们谈论的是JDK Unmodifiable*
与番石榴Immutable*
,实际上区别也在于性能。如果不可变集合不是常规集合的包装器(JDK实现是包装器),则它们既可以更快也可以提高内存效率。
引用番石榴队:
JDK提供了Collections.unmodifiableXXX方法,但在我们看来,这些方法可以是
<...>
List.of(...)
确实复制了两次!
如上所述,不可修改不像是不可更改的,因为例如,如果不可修改的集合具有由其他对象引用的基础委托集合,并且该对象对其进行了更改,那么不可修改的集合可以被更改。
关于不可变,它甚至没有很好的定义。但是,通常这意味着对象“不会更改”,但是需要递归定义。例如,我可以在类的实例变量全部为基元且其方法都不包含任何参数且返回基元的类上定义不可变。然后,这些方法递归地允许实例变量不可变,并且所有方法都包含不可变且返回不可变值的参数。该方法应保证随时间返回相同的值。
假设我们可以做到这一点,那么还有概念线程安全。您可能会被认为是不变的(或者随着时间的推移不会改变)也意味着线程安全。 但是事实并非如此这是我在这里提出的要点,但其他答案中尚未提到。我可以构造一个始终返回相同结果但不是线程安全的不可变对象。为了看到这一点,我假设我通过保持一段时间内的添加和删除来构造一个不可变的集合。现在,不可变集合通过查看内部集合(它可能会随着时间变化),然后(内部)添加和删除在创建集合后添加或删除的元素来返回其元素。显然,尽管该集合总是返回相同的元素,但它并不是线程安全的,仅因为它永远不会改变值。
现在我们可以将不可变定义为线程安全且永不更改的对象。存在创建通常导致此类的不可变类的准则,但是,请记住,可能存在创建不可变类的方法,这些方法需要注意线程安全性,例如,如上面“快照”集合示例中所述。