在测试 guava ImmutableList 行为时感到困惑
Confused while testing guava ImmutableList behavior
public class TestImmutableCollection {
static class Helper {
int val;
public Helper(int val) {
this.val = val;
}
@Override
public String toString() {
return String.valueOf(val);
}
}
public static void main(String[] args) {
List<Helper> origin = new ArrayList<>();
origin.add(new Helper(10));
origin.add(new Helper(11));
origin.add(new Helper(13));
ImmutableList<Helper> ul = ImmutableList.copyOf(origin);
ul.get(0).val = 15;
System.out.println(ul);
System.out.println(origin);
}
}
我在之前的采访中被问及不变性,因此我在 Internet 上搜索 Java 中的不可变集合。所以我 运行 进入这个 post Java Immutable Collections 那里有很多人提到 Guava 有更好更安全的不可变集合实现。
在使用 guava 之前,我已经使用 JDK 的内置 UnmodifiableList 测试了上面的代码,结果证明 UnmodifiableList 只是原始列表的包装器,因此如果我使用 get to,两个列表的内容都会更新访问内部元素,然后更新元素对象的字段。
正如前面的人所说 post:
Unlike Collections.unmodifiableList(java.util.List<? extends T>),
which is a view of a separate collection that can still change, an
instance of ImmutableList contains its own private data and will never
change.
然后我用guava ImmutableList测试代码。但它仍然给出了相同的结果。 copyOf()
创建的ImmutableList和原来的列表内容都变了
我很困惑为什么会变成这样。我在这里了解不变性的范围吗? Collection中元素内容的改变不会在这里判断为Collection的改变?但是番石榴的文档说得非常绝对,它永远不会改变。
如果是这样,在这种情况下guava ImmutableList和JDK的UnmodifiableList有什么区别?
希望有人能对此有所启发。对此表示赞赏。
更新: 好吧,我知道不对 class 的字段添加任何访问限制不是一个好的设计。但是只要尝试想象一个现实的案例,其中 Helper 可以像用户的 Account,字段可以是用户的 username,你会肯定提供更新这个用户名字段的方法吧?那么在这种情况下,我怎样才能只显示列表中帐户的信息而不让调用者修改此列表中元素的内容?
您修改的不是列表,而是列表中的元素之一!
如果您执行以下操作
public static void main(String[] args) {
List<Helper> origin = new ArrayList<>();
origin.add(new Helper(10));
origin.add(new Helper(11));
origin.add(new Helper(13));
ImmutableList<Helper> ul = ImmutableList.copyOf(origin);
ul.get(0).val = 15;
origin.add(new Helper(42)); // <-- Added another element here!
System.out.println(ul);
System.out.println(origin);
}
您将得到以下输出:
[15, 11, 13]
[15, 11, 13, 42]
要真正实现不可变性,您应该考虑也让您的元素不可变。
UnmodifiableList
和 ImmutableList
都不能保证存储在这些集合中的元素永远不会改变。集合本身是不可变的,而不是存储在其中的元素。这些集合必须 return 存储元素的副本,但事实并非如此。您不能 add/remove 这些集合中的元素,但您仍然可以修改元素本身。
Javadocs 对 ImmutableCollection
的引用对这里有很大帮助:
Shallow immutability. Elements can never be added, removed or replaced in this collection. This is a stronger guarantee than that of Collections.unmodifiableCollection(java.util.Collection<? extends T>)
, whose contents change whenever the wrapped collection is modified.
基本上 UnmodifiableList
只是一些其他集合的包装器。如果您以某种方式获得了那个包装的集合,您可以对其进行修改(添加或删除元素),这些更改将反映在 UnmodifiableList
本身中。
另一方面,ImmutableList
复制对原始集合所有元素的引用,不再使用它。新创建的 ImmutableList
和原始集合因此是分开的,您可以修改原始集合(添加或删除元素)并且不会在 ImmutableList
.
中看到任何变化
以及 Javadocs 对 ImmutableCollection
的额外引述:
Warning: as with any collection, it is almost always a bad idea to modify an element (in a way that affects its Object.equals(java.lang.Object)
behavior) while it is contained in a collection. Undefined behavior and bugs will result. It's generally best to avoid using mutable objects as elements at all, as many users may expect your "immutable" object to be deeply immutable.
编辑 以回答您添加的问题:
如果您希望调用者能够对这些对象执行任何操作,但不希望这些更改影响您的原始数据,您可以进行深度复制 - 复制集合中的每个元素。
其他方法是为 Account
class 编写一个包装器,访问受限 - 没有任何设置器。通过简单的组合,您可以阻止所有修改。
public class TestImmutableCollection {
static class Helper {
int val;
public Helper(int val) {
this.val = val;
}
@Override
public String toString() {
return String.valueOf(val);
}
}
public static void main(String[] args) {
List<Helper> origin = new ArrayList<>();
origin.add(new Helper(10));
origin.add(new Helper(11));
origin.add(new Helper(13));
ImmutableList<Helper> ul = ImmutableList.copyOf(origin);
ul.get(0).val = 15;
System.out.println(ul);
System.out.println(origin);
}
}
我在之前的采访中被问及不变性,因此我在 Internet 上搜索 Java 中的不可变集合。所以我 运行 进入这个 post Java Immutable Collections 那里有很多人提到 Guava 有更好更安全的不可变集合实现。 在使用 guava 之前,我已经使用 JDK 的内置 UnmodifiableList 测试了上面的代码,结果证明 UnmodifiableList 只是原始列表的包装器,因此如果我使用 get to,两个列表的内容都会更新访问内部元素,然后更新元素对象的字段。
正如前面的人所说 post:
Unlike Collections.unmodifiableList(java.util.List<? extends T>), which is a view of a separate collection that can still change, an instance of ImmutableList contains its own private data and will never change.
然后我用guava ImmutableList测试代码。但它仍然给出了相同的结果。 copyOf()
创建的ImmutableList和原来的列表内容都变了
我很困惑为什么会变成这样。我在这里了解不变性的范围吗? Collection中元素内容的改变不会在这里判断为Collection的改变?但是番石榴的文档说得非常绝对,它永远不会改变。
如果是这样,在这种情况下guava ImmutableList和JDK的UnmodifiableList有什么区别?
希望有人能对此有所启发。对此表示赞赏。
更新: 好吧,我知道不对 class 的字段添加任何访问限制不是一个好的设计。但是只要尝试想象一个现实的案例,其中 Helper 可以像用户的 Account,字段可以是用户的 username,你会肯定提供更新这个用户名字段的方法吧?那么在这种情况下,我怎样才能只显示列表中帐户的信息而不让调用者修改此列表中元素的内容?
您修改的不是列表,而是列表中的元素之一!
如果您执行以下操作
public static void main(String[] args) {
List<Helper> origin = new ArrayList<>();
origin.add(new Helper(10));
origin.add(new Helper(11));
origin.add(new Helper(13));
ImmutableList<Helper> ul = ImmutableList.copyOf(origin);
ul.get(0).val = 15;
origin.add(new Helper(42)); // <-- Added another element here!
System.out.println(ul);
System.out.println(origin);
}
您将得到以下输出:
[15, 11, 13]
[15, 11, 13, 42]
要真正实现不可变性,您应该考虑也让您的元素不可变。
UnmodifiableList
和 ImmutableList
都不能保证存储在这些集合中的元素永远不会改变。集合本身是不可变的,而不是存储在其中的元素。这些集合必须 return 存储元素的副本,但事实并非如此。您不能 add/remove 这些集合中的元素,但您仍然可以修改元素本身。
Javadocs 对 ImmutableCollection
的引用对这里有很大帮助:
Shallow immutability. Elements can never be added, removed or replaced in this collection. This is a stronger guarantee than that of
Collections.unmodifiableCollection(java.util.Collection<? extends T>)
, whose contents change whenever the wrapped collection is modified.
基本上 UnmodifiableList
只是一些其他集合的包装器。如果您以某种方式获得了那个包装的集合,您可以对其进行修改(添加或删除元素),这些更改将反映在 UnmodifiableList
本身中。
ImmutableList
复制对原始集合所有元素的引用,不再使用它。新创建的 ImmutableList
和原始集合因此是分开的,您可以修改原始集合(添加或删除元素)并且不会在 ImmutableList
.
以及 Javadocs 对 ImmutableCollection
的额外引述:
Warning: as with any collection, it is almost always a bad idea to modify an element (in a way that affects its
Object.equals(java.lang.Object)
behavior) while it is contained in a collection. Undefined behavior and bugs will result. It's generally best to avoid using mutable objects as elements at all, as many users may expect your "immutable" object to be deeply immutable.
编辑 以回答您添加的问题:
如果您希望调用者能够对这些对象执行任何操作,但不希望这些更改影响您的原始数据,您可以进行深度复制 - 复制集合中的每个元素。
其他方法是为 Account
class 编写一个包装器,访问受限 - 没有任何设置器。通过简单的组合,您可以阻止所有修改。