Tom*_*ine 10 java collections copy-constructor
过去,我说过要安全地复制集合,请执行以下操作:
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
Run Code Online (Sandbox Code Playgroud)
或者
public static void doThing(NavigableSet<String> strs) {
NavigableSet<String> newStrs = new TreeSet<>(strs);
Run Code Online (Sandbox Code Playgroud)
但是这些“复制”构造函数,类似的静态创建方法和流,真的安全吗,规则在哪里指定?我所说的安全是指Java 语言提供的基本语义完整性保证和针对恶意调用者强制执行的集合,假设有合理的支持SecurityManager
并且没有缺陷。
我很高兴与方法投掷ConcurrentModificationException
,NullPointerException
,IllegalArgumentException
,ClassCastException
,等,或者甚至挂起。
我选择String
了一个不可变类型参数的例子。对于这个问题,我对具有自身问题的可变类型集合的深拷贝不感兴趣。
Hol*_*ger 13
对于在普通 API(例如 Collection API)中的同一 JVM 中运行的故意恶意代码,没有真正的保护。
很容易证明:
public static void main(String[] args) throws InterruptedException {
Object[] array = { "foo", "bar", "baz", "and", "another", "string" };
array[array.length - 1] = new Object() {
@Override
public String toString() {
Collections.shuffle(Arrays.asList(array));
return "string";
}
};
doThing(new ArrayList<String>() {
@Override public Object[] toArray() {
return array;
}
});
}
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
System.out.println("made a safe copy " + newStrs);
for(int i = 0; i < 10; i++) {
System.out.println(newStrs);
}
}
Run Code Online (Sandbox Code Playgroud)
public static void main(String[] args) throws InterruptedException {
Object[] array = { "foo", "bar", "baz", "and", "another", "string" };
array[array.length - 1] = new Object() {
@Override
public String toString() {
Collections.shuffle(Arrays.asList(array));
return "string";
}
};
doThing(new ArrayList<String>() {
@Override public Object[] toArray() {
return array;
}
});
}
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
System.out.println("made a safe copy " + newStrs);
for(int i = 0; i < 10; i++) {
System.out.println(newStrs);
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,期望 aList<String>
并不能保证实际获得String
实例列表。由于类型擦除和原始类型,在列表实现方面甚至无法修复。
另一件事,你可以责怪ArrayList
的构造函数,是对传入集合toArray
实现的信任。TreeMap
不会以同样的方式受到影响,而仅仅是因为传递数组没有这样的性能提升,就像在ArrayList
. 这两个类都不能保证构造函数中的保护。
通常,在每个角落都假设故意编写恶意代码的尝试编写代码是没有意义的。它可以做的太多,以防止一切。这种保护只对确实封装了一个动作的代码有用,该动作可以让恶意调用者访问某些东西,如果没有这些代码,它就无法访问。
如果您需要特定代码的安全性,请使用
public static void doThing(List<String> strs) {
String[] content = strs.toArray(new String[0]);
List<String> newStrs = new ArrayList<>(Arrays.asList(content));
System.out.println("made a safe copy " + newStrs);
for(int i = 0; i < 10; i++) {
System.out.println(newStrs);
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以确定newStrs
它只包含字符串,并且在构造后不能被其他代码修改。
或List<String> newStrs = List.of(strs.toArray(new String[0]));
与 Java 9 或更新版本一起使用
请注意,Java 10 的List.copyOf(strs)
功能相同,但其文档并未声明保证不信任传入集合的toArray
方法。所以调用List.of(…)
,它肯定会复制一份,以防它返回一个基于数组的列表,更安全。
由于没有调用者可以改变方式,数组工作,将传入的集合转储到数组中,然后用它填充新集合,将始终使副本安全。由于集合可以保存对返回数组的引用,如上所示,它可以在复制阶段更改它,但不能影响集合中的副本。
因此,任何一致性检查都应该在从数组或整个结果集合中检索到特定元素之后进行。
归档时间: |
|
查看次数: |
450 次 |
最近记录: |