Java 不可变集合
来自 Java 1.6 集合框架文档:
不支持任何修改操作(例如
添加
、删除
和清除
)的集合称为不可修改< /em>. [...] 另外保证 Collection 对象中的任何更改都不可见的集合称为不可变。
第二个标准让我有点困惑。假设第一个集合是不可修改的,并且假设原始集合引用已被丢弃,那么第二行中引用的更改是什么?它是指集合中保存的元素的变化,即元素的状态吗?
第二个问题:
对于一个不可变的集合,如何提供指定的额外保证?如果集合中元素的状态由线程更新,那么这些更新是否足以保证不可变性?状态在持有不可变集合的线程上不可见?
From Java 1.6 Collection Framework documentation:
Collections that do not support any modification operations (such as
add
,remove
andclear
) are referred to as unmodifiable. [...] Collections that additionally guarantee that no change in the Collection object will ever be visible are referred to as immutable.
The second criteria confuses me a bit. Given the first collection is unmodifiable, and assuming that the original collection reference has been disposed away, what are the changes that are referred to in the second line? Is it referring to the changes in the elements held in the collection ie the state of the elements?
Second question:
For a collection to be immutable, how does one go about providing the additional guarantees specified? If the state of an element in the collection is updated by a thread, is it sufficient for immutability that those updates in the state are not visible on the thread holding the immutable collection?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
不可修改的集合通常是其他集合的只读视图(包装器)。您无法添加、删除或清除它们,但底层集合可以更改。
不可变集合根本无法更改——它们不包装另一个集合——它们有自己的元素。
以下是 guava 的
不可变列表
因此,基本上,为了从可变集合中获取不可变集合,您必须将其元素复制到新集合,并禁止所有操作。
Unmodifiable collections are usually read-only views (wrappers) of other collections. You can't add, remove or clear them, but the underlying collection can change.
Immutable collections can't be changed at all - they don't wrap another collection - they have their own elements.
Here's a quote from guava's
ImmutableList
So, basically, in order to get an immutable collection out of a mutable one, you have to copy its elements to the new collection, and disallow all operations.
不同之处在于,您无法引用允许更改的不可变集合。不可修改的集合通过该引用是不可修改的,但某些其他对象可能指向可以更改它的相同数据。
例如
The difference is that you can't have a reference to an immutable collection which allows changes. Unmodifiable collections are unmodifiable through that reference, but some other object may point to the same data through which it can be changed.
e.g.
c1
是可变的(即既不是不可修改也不是不可变)。c2
是不可修改:它本身无法更改,但如果稍后我更改c1
,那么那个就会更改将在c2
中可见。这是因为
c2
只是c1
的包装器,而不是真正的独立副本。 Guava 提供了ImmutableList接口
和一些实现。这些工作实际上是创建输入的副本(除非输入本身是一个不可变的集合)。
关于你的第二个问题:
集合的可变性/不可变性并不依赖于其中包含的对象的可变性/不可变性。对于此描述,修改集合中包含的对象不算作“集合的修改”。当然,如果您需要一个不可变的集合,您通常也希望它包含不可变的对象。
c1
is mutable (i.e. neither unmodifiable nor immutable).c2
is unmodifiable: it can't be changed itself, but if later on I changec1
then that change will be visible inc2
.This is because
c2
is simply a wrapper aroundc1
and not really an independent copy. Guava provides theImmutableList
interface and some implementations. Those work by actually creating a copy of the input (unless the input is an immutable collection on its own).Regarding your second question:
The mutability/immutability of a collection does not depend on the mutability/immutability of the objects contained therein. Modifying an object contained in a collection does not count as a "modification of the collection" for this description. Of course if you need a immutable collection, you usually also want it to contain immutable objects.
现在,java 9 具有用于 Immutable List、Set、Map 和 Map.Entry 的工厂方法。
在 Java SE 8 及更早版本中,我们可以使用 Collections 类实用方法(如 unmodifierXXX)来创建 Immutable Collection 对象。
然而,这些 Collections.unmodifyingXXX 方法是非常乏味且冗长的方法。为了克服这些缺点,Oracle 公司向 List、Set 和 Map 接口添加了一些实用方法。
现在是 java 9 :-
List 和 Set 接口具有“of()”方法来创建空或非空的 Immutable List 或 Set 对象,如下所示:
空列表示例
非空列表示例
Now java 9 has factory Methods for Immutable List, Set, Map and Map.Entry .
In Java SE 8 and earlier versions, We can use Collections class utility methods like unmodifiableXXX to create Immutable Collection objects.
However these Collections.unmodifiableXXX methods are very tedious and verbose approach. To overcome those shortcomings, Oracle corp has added couple of utility methods to List, Set and Map interfaces.
Now in java 9 :-
List and Set interfaces have “of()” methods to create an empty or no-empty Immutable List or Set objects as shown below:
Empty List Example
Non-Empty List Example
我认为这里的要点是,即使集合是不可修改的,也不能确保它不能更改。举个例子,如果元素太旧,集合就会将其逐出。不可修改只是意味着持有引用的对象不能更改它,而不是它不能更改。一个真实的例子是
Collections.unmodifyingList
方法。它返回一个不可修改的列表视图。传递到此方法中的 List 引用仍然是可修改的,因此该列表可以由传递的引用的任何持有者进行修改。这可能会导致 ConcurrentModificationException 和其他不好的事情。不可变意味着集合无论如何都不能改变。
第二个问题:Immutable集合并不意味着集合中包含的对象不会改变,只是集合所包含的对象的数量和组成不会改变。换句话说,馆藏的参考文献列表不会改变。这并不意味着被引用的对象的内部不能改变。
I believe the point here is that even if a collection is Unmodifiable, that does not ensure that it cannot change. Take, for example, a collection that evicts elements if they are too old. Unmodifiable just means that the object holding the reference cannot change it, not that it cannot change. A true example of this is the
Collections.unmodifiableList
method. It returns an unmodifiable view of a List. The List reference that was passed into this method is still modifiable and so the list can be modified by any holder of the reference that was passed. This can result in ConcurrentModificationExceptions and other bad things.Immutable means that in no way can the collection be changed.
Second question: An Immutable collection does not mean that the objects contained in the collection will not change, just that collection will not change in the number and composition of objects that it holds. In other words, the collection's list of references will not change. That does not mean that the internals of the object being referenced cannot change.
Pure4J 通过两种方式支持您所追求的目标。
首先,它提供了一个@ImmutableValue注释,这样你就可以注释一个类来表明它是不可变的。有一个 Maven 插件可让您检查代码是否实际上是不可变的(使用
final
等)。其次,它提供了来自 Clojure 的持久集合(添加了泛型),并确保添加到集合中的元素是不可变的。这些性能显然相当不错。集合都是不可变的,但实现 java 集合接口(和泛型)以进行检查。突变返回新的集合。
免责声明:我是这个的开发者
Pure4J supports what you are after, in two ways.
First, it provides an
@ImmutableValue
annotation, so that you can annotate a class to say that it is immutable. There is a maven plugin to allow you to check that your code actually is immutable (use offinal
etc.).Second, it provides the persistent collections from Clojure, (with added generics) and ensures that elements added to the collections are immutable. Performance of these is apparently pretty good. Collections are all immutable, but implement java collections interfaces (and generics) for inspection. Mutation returns new collections.
Disclaimer: I'm the developer of this
在 Java 9 之前,Collections.unmodifyingXXX() 方法用于创建不可修改的集合。这些方法的行为就像包装方法一样,返回原始集合的不可修改视图或只读视图。即,您无法通过这些包装器方法返回的引用执行添加、删除、替换、清除等修改操作。但是,如果您有其他对原始集合的引用,则可以修改原始集合,并且这些修改将反映在这些方法返回的视图中。
例如,
从 Java 9 开始,引入静态工厂方法来创建不可变集合。
不可变与不可修改:
Java 9 不可变集合和 Collections.unmodifyingXXX() 包装方法返回的不可修改集合不同。不可修改的集合只是原始集合的只读视图。您可以对原始集合执行修改操作,这些修改将反映在这些方法返回的集合中。但是,Java 9 静态工厂方法返回的不可变集合是 100% 不可变的。它们一旦创建就无法修改。
来源:https://javaconceptoftheday.com/java-9-immutable-collections/
Before Java 9, Collections.unmodifiableXXX() methods are used to create unmodifiable collections. These methods just behave like wrapper methods which return unmodifiable view or read-only view of the original collection. i.e you can’t perform modifying operations like add, remove, replace, clear etc through the references returned by these wrapper methods. But, you can modify original collection if you have other references to it and those modifications will be reflected in the view returned by these methods.
For example,
From Java 9, static factory methods are introduced to create immutable collections.
Immutable Vs Unmodifiable :
Java 9 Immutable collections and unmodifiable collections returned by the Collections.unmodifiableXXX() wrapper methods are not the same. Unmodifiable collections are just the read-only views of the original collection. You can perform modifying operations on the original collection and those modifications will be reflected in the collections returned by these methods. But, immutable collections returned by Java 9 static factory methods are 100% immutable. You can’t modify them once they are created.
Source : https://javaconceptoftheday.com/java-9-immutable-collections/
我对这里的答案印象不深。
不可修改的集合
正如java文档所述不可修改的集合(强调我的):
您可以通过调用
List.copyOf()
或List.of()
。如果即使在通过这些方法获得的空列表上调用
.removeIf(x -> true)
,程序也会抛出UnsupportedOperationException
。不可修改的视图集合
不可修改的集合也不一定是视图。相同的 Java 文档页面分别引用它:
因此,一个集合必须既不可修改,又是一个有资格成为不可修改视图的视图。您可以通过调用
Collections.unmodifying*
方法获得不可修改的视图。如果后备列表不可修改,则通过
.subList()
获取的列表也将是不可修改的视图。不可修改的视图的行为和抛出的异常与上面提到的完全相同。不可变集合
现在,到奖励部分。我也喜欢称它们为智能集合——它有助于直观地记住差异。
您可以通过调用
Collections.empty*()
方法或Collections.singleton()
来获取它们。如果方法的执行对给定参数没有影响(如果参数是可变的),那么它们不需要抛出 UnsupportedOperationException(通常也不会)。我们以前面的
.removeIf(x -> true)
为例,并在Collections.emptyList()
上调用它。尽管列表是不可变的,但由于列表是空的(即使它是可变的),所以无论如何也没有什么可以删除的,所以它不会抛出异常。I am not impressed by answers here.
Unmodifiable collections
As java docs state about Unmodifiable Collections (emphasis mine):
You can get an unmodifiable list by calling
List.copyOf()
orList.of()
.If you call
.removeIf(x -> true)
even on an empty list obtained by those methods, the program will throwUnsupportedOperationException
.Unmodifiable view collections
Nor unmodifiable collections are necessarily views. The same Java docs page refers to it separately:
So, a collection has to be both - unmodifiable and a view to qualify as an unmodifiable view. You get unmodifiable views by calling
Collections.unmodifiable*
methods.If backing list is unmodifiable, the list obtained by
.subList()
will also be an unmodifiable view. Unmodifiable views behave and throw exactly the same exceptions as mentioned above.Immutable collections
Now, to the bonus part. I also like to call them smart collections - it helps to memorize the difference intuitively.
You get them by calling
Collections.empty*()
methods orCollections.singleton()
.They don't have a requirement to throw
UnsupportedOperationException
(and usually don't) if the execution of its method would have no effect with a given parameter if it was mutable. Let's take a previous example with.removeIf(x -> true)
and call it onCollections.emptyList()
. Although the list is immutable, there is nothing to remove anyway since the list is empty (even if it was mutable), so it doesn't throw.