在上一个问题中 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
方法?
编辑1:
正如@aUserHimself 所建议的,我已经尝试(但没有成功)衡量 System.arraycopy
的性能。针对 Stream
界面。下面我提供了基准代码及其结果:
@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
有没有人知道任何其他可能的方法?
编辑2:
我已经根据建议的修改更新了基准测试代码和结果,以使比较更加均衡。但是,对于较大的实验,存在一些错误(可能是由于我认为的堆大小),对于较小的实验,Arraycopy
仍然优于 Stream
一。 所以我认为当目标大小不同时没有比使用 arraycopy
更好的复制数组的方法了。 .
最佳答案
您可以尝试根据 java8
的 Streams 来衡量性能同样,当您需要过滤掉或向 destination
数组添加新元素时,它也很有用:
public static void copyBigArrayAndFilter() {
long time = System.currentTimeMillis();
int[] original = IntStream.range(0, 10_000).toArray();
int[] destination = Arrays.stream(original).filter(i -> i > 1_000 && i < 9_000).toArray();
System.out.println("Original size: " + original.length);
System.out.println("Destination size: " + destination.length);
System.out.println("Duration: " + (System.currentTimeMillis() - time) + " ms." );
}
public static void copyBigArrayAndAdd() {
long time = System.currentTimeMillis();
int[] original = IntStream.range(0, 10_000).toArray();
int[] destination = Arrays.stream(original).flatMap(i -> Arrays.stream(original).map(j -> i + j)).toArray();
System.out.println("Original size: " + original.length);
System.out.println("Destination size: " + destination.length);
System.out.println("Duration: " + (System.currentTimeMillis() - time) + " ms." );
}
更新:
我自己不是专家,但你的问题很有趣,我只是想使用 streams
以防你之前对 original
数组进行处理它被复制到 destination
。 (参见 copyBigger
示例)
对于copySmaller
的例子,我们在做不同的操作:你是将original
的前半部分复制到destination
中,我是复制元素大于 length/2
,在我的例子中需要对 original
进行完整迭代。您将如何使用 System.arraycopy
实现它?
对于 SystemArraycopyBigger
,您只是将 destination
数组大小设置为 original
的两倍,但您只是复制了 size
最后。请注意,在我的 StreamArraycopyBigger
中,destination
数组具有 size ^ 2
元素,而不是 size * 2
:对于每个元素在 original
数组中,我们有额外的 size
个元素。
最终结果可能不会有太大变化,但如果你想测试等效操作而不是比较苹果和橘子,请改用这个。
此外,为什么不尝试更大的样本量,例如 10_000
或 1_000_000
?
@Benchmark
public int[] SystemArraycopyBigger() {
int i;
final int length = this.size;
int[] destination = new int[length * length];
for(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;
}
我不确定这是否是您想要的。但我想指出的是:如果您已经处理了数组,那么您几乎不可能比 System.arraycopy
做得更好。但是,如果您需要修改/处理并且只复制其中的一部分,streams
可能会更快。
关于java - 当目标数组大小不同时 System.arraycopy 的替代方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46295070/