【发布时间】:2018-02-27 22:07:45
【问题描述】:
在上一个问题中Why clone() is the best way for copying arrays?@procrastinator 证明给定一个数组,original.clone() 平均比System.arraycopy(original, 0, destination, 0, length) 快两倍
但是我注意到,当使用clone 方法时,不能修改目标数组的长度,也不能只复制数组的一部分。使用System.arraycopy 我会这样做:
具有额外位置的新数组
int[] original = new int[] {1,2,3,4,5};
int originalSize = original.length;
int newSize = originalSize + 1;
int[] destination = new int[newSize];
System.arraycopy(original, 0, destination, 0)
destination[newSize - 1] = newValue;
位置更少的新数组
int[] original = new int[] {1,2,3,4,5};
int originalSize = original.length;
int newSize = originalSize - 1;
int[] destination = new int[newSize];
System.arraycopy(original, 1, destination, 0)
(请注意,为了清楚起见,示例中的数组很小,但在实际情况下它们更大)
有没有办法在任何场景中实现与clone 类似的性能?还是在这些情况下我们必须使用System.arraycopy 方法?
EDIT1:
正如@aUserHimself 所建议的那样,我已经尝试(没有任何成功)测量System.arraycopy 与Stream 接口的性能。下面我提供 Benchmark 代码及其结果:
@Benchmark
public int[] SystemArraycopySmaller() {
final int length = this.size;
int[] destination = new int[length / 2];
System.arraycopy(this.original, 0, destination, 0, length / 2);
return destination;
}
@Benchmark
public int[] StreamArraycopySmaller() {
final int length = this.size;
int[] destination = Arrays.stream(this.original).filter(i -> i < length / 2).toArray();
return destination;
}
@Benchmark
public int[] SystemArraycopyBigger() {
final int length = this.size;
int[] destination = new int[length * length];
for (int i = 0; i < length; i++) {
System.arraycopy(this.original, 0, destination, i * length, length);
}
return destination;
}
@Benchmark
public int[] StreamArraycopyBigger() {
int[] destination = Arrays.stream(this.original).flatMap(i -> Arrays.stream(this.original).map(j -> j)).toArray();
return destination;
}
结果:
Benchmark (size) Mode Cnt Score Error Units
SampleBenchmark.StreamArraycopyBigger 10000 thrpt 10 ≈ 0 ops/s
SampleBenchmark.StreamArraycopyBigger 1000 thrpt 10 ≈ 0 ops/s
SampleBenchmark.StreamArraycopyBigger 100 thrpt 10 11,997 ± 0,002 ops/s
SampleBenchmark.StreamArraycopyBigger 10 thrpt 10 608,899 ± 8,975 ops/s
SampleBenchmark.StreamArraycopyBigger 1 thrpt 10 6373,457 ± 313,626 ops/s
SampleBenchmark.StreamArraycopySmaller 10000 thrpt 10 36,692 ± 0,728 ops/s
SampleBenchmark.StreamArraycopySmaller 1000 thrpt 10 328,875 ± 2,259 ops/s
SampleBenchmark.StreamArraycopySmaller 100 thrpt 10 2141,368 ± 8,832 ops/s
SampleBenchmark.StreamArraycopySmaller 10 thrpt 10 9018,659 ± 118,933 ops/s
SampleBenchmark.StreamArraycopySmaller 1 thrpt 10 12954,709 ± 114,621 ops/s
SampleBenchmark.SystemArraycopyBigger 10000 thrpt 10 ≈ 0 ops/s
SampleBenchmark.SystemArraycopyBigger 1000 thrpt 10 ≈ 0 ops/s
SampleBenchmark.SystemArraycopyBigger 100 thrpt 10 161,004 ± 1,361 ops/s
SampleBenchmark.SystemArraycopyBigger 10 thrpt 10 10039,397 ± 123,553 ops/s
SampleBenchmark.SystemArraycopyBigger 1 thrpt 10 42539,869 ± 1965,589 ops/s
SampleBenchmark.SystemArraycopySmaller 10000 thrpt 10 399,816 ± 6,503 ops/s
SampleBenchmark.SystemArraycopySmaller 1000 thrpt 10 3189,271 ± 117,936 ops/s
SampleBenchmark.SystemArraycopySmaller 100 thrpt 10 22533,102 ± 183,870 ops/s
SampleBenchmark.SystemArraycopySmaller 10 thrpt 10 45577,443 ± 1656,788 ops/s
SampleBenchmark.SystemArraycopySmaller 1 thrpt 10 41657,519 ± 183,266 ops/s
有人知道其他可能的方法吗?
EDIT2:
我已根据建议的修改更新了基准代码和结果,以便进行比较。但是,对于较大的实验,存在一些错误(可能是由于我想的堆大小),对于较小的实验,Arraycopy 仍然优于Stream。 所以我认为当目标大小不同时,没有比使用arraycopy 复制数组更好的方法了。。
【问题讨论】:
-
@nullpointer 谢谢。查看我更新的问题
-
我会说不,没有办法达到与
clone类似的性能。还有Arrays.copyOf,但它只是在内部使用arraycopy。 -
当然,如果您的性能热点是复制数组,那么您可能使用了错误的语言来完成这项工作?
-
@Kayaman 这不是我项目中唯一的性能问题,但假设我目前无法更改语言,因为我受益于其他 Java 实用程序(该项目有 100k loc)
-
这是您项目中最重要的性能问题吗?