从集合框架概述:
不支持修改操作(如添加、删除和清除)的集合被称为不可修改的。不是不可修改的集合是可修改的。 另外保证Collection对象中的任何更改都不可见的集合称为不可变集合。不是不可变的集合是可变的。
我不明白其中的区别。 这里unmodifiable和immutable的区别是什么?
从集合框架概述:
不支持修改操作(如添加、删除和清除)的集合被称为不可修改的。不是不可修改的集合是可修改的。 另外保证Collection对象中的任何更改都不可见的集合称为不可变集合。不是不可变的集合是可变的。
我不明白其中的区别。 这里unmodifiable和immutable的区别是什么?
不可修改的集合通常是其他代码仍然可以访问的可修改集合的包装器。因此,如果您仅拥有对不可修改集合的引用,则不能对其进行任何更改,但不能指望内容不会更改。
不可变集合保证没有任何东西可以再改变集合。如果它包装了一个可修改的集合,它将确保没有其他代码可以访问该可修改的集合。注意,尽管没有代码可以改变集合包含引用的对象,但对象本身仍然可能是可变的——创建一个不可变的StringBuilder集合并不会以某种方式“冻结”那些对象。
基本上,区别在于其他代码是否能够在您背后更改集合。
我认为主要的区别是,可变集合的所有者可能希望向其他一些代码提供对集合的访问,但通过不允许其他代码修改集合的接口提供这种访问(同时将这种能力保留给所有者代码)。因此集合不是不可变的,但某些用户不允许更改集合。
Oracle的Java Collection Wrapper教程有这样说(强调添加):
不可修改的包装器有以下两个主要用途: 使集合在构建后不可变。在这种情况下,最好不要维护对后台的引用 收集。这绝对保证了不变性。 允许某些客户端只读访问您的数据结构。您保留了对备份集合的引用 输出对包装器的引用。这样,客户可以看,但不能看 修改,同时保持完全访问权限。
基本上不可修改集合是一个视图,所以它仍然可以间接地从其他一些可修改的引用中“修改”。也因为它只是另一个集合的只读视图,当源集合发生变化时,不可修改集合将始终显示最新的值。
然而,不可变集合可以被视为另一个集合的只读副本,不能被修改。在这种情况下,当源集合发生变化时,不可变集合不反映这些变化
下面是一个测试用例来可视化这种差异。
@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]
--
如上所述,不可修改的不像不可变的,因为不可修改的集合可以被改变,例如,如果一个不可修改的集合有一个被其他对象引用的底层委托集合,而该对象改变了它。
Regarding immutable, it's not even well defined. However, generally it means that the object "will not change", but that would need to be defined recursively. For example, I can define immutable on classes whose instance variables are all primitives and whose methods all contain no arguments and return primitives. The methods then recursively allow the instance variables to be immutable and all methods to contain arguments that are immutable and that return immutable values. The methods should be guaranteed to return the same value over time.
Assuming that we can do that, there is also the concept thread safe. And you might be led to believe that immutable (or not changeble over time) also implies thread safe. However that is not the case and that is the main point I am making here that has not yet been noted in other answers. I can construct an immutable object that always returns the same results yet is not thread safe. To see this suppose that I construct an immutable collection by maintaining additions and deletions over time. Now the immutable collection returns its elements by looking at the internal collection (which may be changing over time) and then (internally) adding and deleting the elements that were added or deleted after creation of the collection. Clearly, although the collection would always return the same elements, it is not thread safe merely because it will never change value.
现在我们可以将不可变定义为线程安全且永远不会改变的对象。有一些创建不可变类的指导原则,这些类通常会导致这样的类,然而,请记住,可能有一些创建不可变类的方法,需要注意线程安全,例如,如上面的“快照”集合示例所述。
如果我们讨论的是JDK不可修改*和番石榴不可修改*,实际上它们的区别还在于性能。如果不可变集合不是常规集合的包装器(JDK实现就是包装器),那么它们可以更快、更节省内存。 以番石榴团队为例:
JDK提供了集合。不可修改的xxx方法,但在我们看来,这些可以
<…>
低效:数据结构仍然有可变集合的所有开销,包括并发修改检查,哈希表中的额外空间等。
引用Java™教程:
Unlike synchronization wrappers, which add functionality to the wrapped collection, the unmodifiable wrappers take functionality away. In particular, they take away the ability to modify the collection by intercepting all the operations that would modify the collection and throwing an UnsupportedOperationException. Unmodifiable wrappers have two main uses, as follows: To make a collection immutable once it has been built. In this case, it's good practice not to maintain a reference to the backing collection. This absolutely guarantees immutability. To allow certain clients read-only access to your data structures. You keep a reference to the backing collection but hand out a reference to the wrapper. In this way, clients can look but not modify, while you maintain full access.
(强调我的)
这真的很概括。
Java™教程介绍了以下内容:
Unlike synchronization wrappers, which add functionality to the wrapped collection, the unmodifiable wrappers take functionality away. In particular, they take away the ability to modify the collection by intercepting all the operations that would modify the collection and throwing an UnsupportedOperationException. Unmodifiable wrappers have two main uses, as follows: To make a collection immutable once it has been built. In this case, it's good practice not to maintain a reference to the backing collection. This absolutely guarantees immutability. To allow certain clients read-only access to your data structures. You keep a reference to the backing collection but hand out a reference to the wrapper. In this way, clients can look but not modify, while you maintain full access.
我认为这是一个很好的解释,足以理解其中的区别。
[不可更改和不可更改]
不可修改的集合(对象)仍然可以通过改变原始对象来改变。使用参考是可能的。
Java提供了几种方法来创建一个不可修改的映射:
Collections.unmodifiableMap () Java 9 Map.of(), Map.ofEntries()
// normal list
List list1 = new ArrayList();
list1.add(1);
// unmodifiable list
List list2 = Collections.unmodifiableList(list1);
// immutable list
List list3 = Collections.unmodifiableList(new ArrayList<>(list1));
list1.add(2);
list1.add(3);
System.out.println(list1);
System.out.println(list2);
System.out.println(list3);
输出:
[1, 2, 3]
[1, 2, 3]
[1]
一个对象被认为是不可变的,如果它的状态在被构造后不能改变。创建集合的不可变实例后,只要对它的引用存在,它就会保存相同的数据。
不可变集合的一个优点是它是自动线程安全的。包含不可变对象的集合在构造后自动是线程安全的。创建这样一个集合后,可以将它交给多个线程,它们都将看到一致的视图。
然而,不可变对象的集合并不等同于不可变对象的集合。如果所包含的元素是可变的,则可能导致集合的行为不一致或使其内容看起来发生了变化。
简单地说,如果你给可变的东西加上一点不变性,你就得到了可变性。如果你给不可变的东西加上一点可变性,你就得到了可变性。
不可变和不可修改不是一回事:
不可变集合的行为与collections .unmodifiable…包装器。然而,这些集合不是包装器——它们是由类实现的数据结构,任何修改数据的尝试都会引发异常。
如果您创建了一个List并将其传递给集合。unmodifiableelist方法,那么你会得到一个不可修改的视图。底层列表仍然是可修改的,通过返回的list可以看到对它的修改,因此它实际上不是不可变的。
要演示此行为,请创建一个List并将其传递给Collections.unmodifiableList。如果尝试直接添加到不可修改的列表,则抛出UnsupportedOperationException。
但是,如果您更改了原始List,则不会生成错误,并且不可修改的List已被修改。
在这种情况下,要使集合在构建后不可变,最好不要维护对支持集合的引用。这绝对保证了不变性。
此外,允许某些客户端只读访问您的数据结构。您可以保留对支持集合的引用,但可以分发对包装器的引用。通过这种方式,客户端可以查看但不能修改,而您保持完全访问权限。
因此,不可变集合可以包含可变对象,如果这样做,则该集合既不是不可变的,也不是线程安全的。
不可修改的和不可变的集合
创建一个可修改的地图
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.