从集合框架概述:
不支持修改操作(如添加、删除和清除)的集合被称为不可修改的。不是不可修改的集合是可修改的。 另外保证Collection对象中的任何更改都不可见的集合称为不可变集合。不是不可变的集合是可变的。
我不明白其中的区别。 这里unmodifiable和immutable的区别是什么?
从集合框架概述:
不支持修改操作(如添加、删除和清除)的集合被称为不可修改的。不是不可修改的集合是可修改的。 另外保证Collection对象中的任何更改都不可见的集合称为不可变集合。不是不可变的集合是可变的。
我不明白其中的区别。 这里unmodifiable和immutable的区别是什么?
当前回答
不可修改的和不可变的集合
创建一个可修改的地图
Map<String, String> modifiableMap = new HashMap();
modifiableMap.put(“1”,”one”);
modifiableMap.put(“2”,”two”);
modifiableMap.put(“3”,”three”);
从modifiableMap中创建一个unmodifiableMap
Map<String,String> unmodifiableMap = Collections.unmodifiableMap(modifiableMap);
unmodifiableMap.put(“4”,”Four”) ==>Exception
modifiableMap.put(“4”,”Four”); ==>Allowed, this will also reflect now in the unmodifiableMap , because unmodifiableMap() returns a wrapper around modifiableMap.
从modifiableMap创建一个immutableMap
Map<String,String> immutableMap = Collections.immutableMap(modifiableMap);
immutableMap.put(“5”,”Five”) ==>Exception
modifiableMap.put(“5”,”Five”); ==>Allowed, BUT this will NOT reflect now in the immutableMap, because immutableMap() returns a copy of the modifiableMap.
其他回答
一个对象被认为是不可变的,如果它的状态在被构造后不能改变。创建集合的不可变实例后,只要对它的引用存在,它就会保存相同的数据。
不可变集合的一个优点是它是自动线程安全的。包含不可变对象的集合在构造后自动是线程安全的。创建这样一个集合后,可以将它交给多个线程,它们都将看到一致的视图。
然而,不可变对象的集合并不等同于不可变对象的集合。如果所包含的元素是可变的,则可能导致集合的行为不一致或使其内容看起来发生了变化。
简单地说,如果你给可变的东西加上一点不变性,你就得到了可变性。如果你给不可变的东西加上一点可变性,你就得到了可变性。
不可变和不可修改不是一回事:
不可变集合的行为与collections .unmodifiable…包装器。然而,这些集合不是包装器——它们是由类实现的数据结构,任何修改数据的尝试都会引发异常。
如果您创建了一个List并将其传递给集合。unmodifiableelist方法,那么你会得到一个不可修改的视图。底层列表仍然是可修改的,通过返回的list可以看到对它的修改,因此它实际上不是不可变的。
要演示此行为,请创建一个List并将其传递给Collections.unmodifiableList。如果尝试直接添加到不可修改的列表,则抛出UnsupportedOperationException。
但是,如果您更改了原始List,则不会生成错误,并且不可修改的List已被修改。
在这种情况下,要使集合在构建后不可变,最好不要维护对支持集合的引用。这绝对保证了不变性。
此外,允许某些客户端只读访问您的数据结构。您可以保留对支持集合的引用,但可以分发对包装器的引用。通过这种方式,客户端可以查看但不能修改,而您保持完全访问权限。
因此,不可变集合可以包含可变对象,如果这样做,则该集合既不是不可变的,也不是线程安全的。
不可修改的集合通常是其他代码仍然可以访问的可修改集合的包装器。因此,如果您仅拥有对不可修改集合的引用,则不能对其进行任何更改,但不能指望内容不会更改。
不可变集合保证没有任何东西可以再改变集合。如果它包装了一个可修改的集合,它将确保没有其他代码可以访问该可修改的集合。注意,尽管没有代码可以改变集合包含引用的对象,但对象本身仍然可能是可变的——创建一个不可变的StringBuilder集合并不会以某种方式“冻结”那些对象。
基本上,区别在于其他代码是否能够在您背后更改集合。
我认为主要的区别是,可变集合的所有者可能希望向其他一些代码提供对集合的访问,但通过不允许其他代码修改集合的接口提供这种访问(同时将这种能力保留给所有者代码)。因此集合不是不可变的,但某些用户不允许更改集合。
Oracle的Java Collection Wrapper教程有这样说(强调添加):
不可修改的包装器有以下两个主要用途: 使集合在构建后不可变。在这种情况下,最好不要维护对后台的引用 收集。这绝对保证了不变性。 允许某些客户端只读访问您的数据结构。您保留了对备份集合的引用 输出对包装器的引用。这样,客户可以看,但不能看 修改,同时保持完全访问权限。
不可修改的和不可变的集合
创建一个可修改的地图
Map<String, String> modifiableMap = new HashMap();
modifiableMap.put(“1”,”one”);
modifiableMap.put(“2”,”two”);
modifiableMap.put(“3”,”three”);
从modifiableMap中创建一个unmodifiableMap
Map<String,String> unmodifiableMap = Collections.unmodifiableMap(modifiableMap);
unmodifiableMap.put(“4”,”Four”) ==>Exception
modifiableMap.put(“4”,”Four”); ==>Allowed, this will also reflect now in the unmodifiableMap , because unmodifiableMap() returns a wrapper around modifiableMap.
从modifiableMap创建一个immutableMap
Map<String,String> immutableMap = Collections.immutableMap(modifiableMap);
immutableMap.put(“5”,”Five”) ==>Exception
modifiableMap.put(“5”,”Five”); ==>Allowed, BUT this will NOT reflect now in the immutableMap, because immutableMap() returns a copy of the modifiableMap.
基本上不可修改集合是一个视图,所以它仍然可以间接地从其他一些可修改的引用中“修改”。也因为它只是另一个集合的只读视图,当源集合发生变化时,不可修改集合将始终显示最新的值。
然而,不可变集合可以被视为另一个集合的只读副本,不能被修改。在这种情况下,当源集合发生变化时,不可变集合不反映这些变化
下面是一个测试用例来可视化这种差异。
@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]
--