48

如果我写

List<Integer> a1 = Arrays.asList(1, 2, 3);
List<Integer> a2 = Collections.unmodifiableList(a1);

a2是只读的,但如果我写

a1.set(0,10);

然后a2也被修改。

如果在 API 中说:

返回指定集合的​​不可修改视图。此方法允许模块为用户提供对内部集合的“只读”访问权限。

那么,为什么如果我修改原始集合也修改了目标复制的集合?

也许我误解了它的含义,如果是这样,那么编写该集合的防御性副本的方法是什么?

4

9 回答 9

55

是的,你理解正确。这个想法是umodifiableCollection不能直接更改返回的对象,但可以通过其他方式更改(有效地通过直接更改内部集合)。

只要有东西可以访问内部列表,就可以更改“不可修改”的集合。

这就是为什么您通常构建一个不可修改的集合并确保没有任何东西可以进入内部列表:

Collection<Integer> myUmodifiableCollection = Collection.umodifiableCollection(Arrays.asList(1, 2, 3));

由于没有任何东西可以引用Listcreated by asList,因此这是一个真正不可修改的集合。

这种方法的优点是您根本不需要复制原始集合/列表,从而避免了使用内存和计算能力。

Guava提供了ImmutableCollection类(及其子类,例如ImmutableList),它们提供了真正的不可变集合(通常通过复制源代码)。

于 2013-02-27T11:39:41.067 回答
12

也许我误解了它的含义,如果是这样,那么编写该集合的防御性副本的方法是什么?

通常,您会这样使用它:

private List<Integer> a1 = Arrays.asList(1, 2, 3);

public List<Integer> getUnmodifiable() {
    return Collections.unmodifiableList(a1);
}

调用getUnmodifiable 无权访问您的类内部的人(即他们无法访问私有变量a1)将无法修改返回的列表。

于 2013-02-27T11:38:18.237 回答
4

如果您希望在a1没有 Guava 的情况下保持可变并制作它的不可变副本,这是您可以做到的一种方法。

    List<Integer> a1 = Arrays.asList(1, 2, 3);
    List<Integer> a2 = Collections.unmodifiableList(new ArrayList<>(a1));
于 2018-10-28T20:34:59.107 回答
1

这个想法是你不能通过a2.

修改a1列表确实会修改您在其中看到的内容a2- 这是有意的。

只是没有公开的方式来访问列表a1,你应该有你想要的:)

于 2013-02-27T11:37:21.777 回答
1

API说它是内部集合在你的情况下集合不是内部的

关键是,当您在类中有一个私有列表以及该列表的 gettet 时,您可能希望 getter 的调用者无法修改列表,以防万一您必须返回一个不可修改的列表。否则返回的列表只是对您的内部/私人列表的引用,因此可以修改其内容。

于 2013-02-27T11:37:44.730 回答
1

该声明:

Collections.unmodifiableList(a1);

返回原始集合的包装器,其修饰符方法 throw UnsupportedOperationException

包装器是通读的,这意味着如果您修改a1,更改将反映在包装的集合上a2

于 2013-02-27T11:40:08.600 回答
0

a1并将a2引用相同的数据(内存)。

不可修改的部分来了,只有a2作为条目。

如果您传递a2给您期望该方法是幂等的方法,则进行成像。这种情况a2会有所帮助。

总之,您不能使用a2指针修改数据。

于 2013-02-27T11:40:01.627 回答
0

如果您需要不可修改和不可变列表,或者换句话说,源列表的不可修改副本不依赖于其他库,请尝试以下操作:

Collections.unmodifiableList(Collections.list(Collections.enumeration(sourceList)))

Collections.list()从其枚举中复制值。

于 2017-11-22T12:09:13.167 回答
0

从 Java 10 开始,您可以这样做List.copyOf()

List<Integer> a1 = Arrays.asList(1, 2, 3);
List<Integer> a2 = List.copyOf(a1);

之后,a1通过 看不到对 的更改a2,因为会将List.copyOf()列表的内容复制到新的。副本不可修改。但请务必在使用该方法之前阅读有关该方法的文档,因为它不适用于包含null值的列表。

于 2022-01-22T15:17:54.333 回答