在上一个问题中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
方法?
正如@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
有没有人知道任何其他可能的方法?
我已使用建议的修改更新了基准代码和结果,以便进行比较。但是,对于更大的实验,存在一些错误(可能是由于我认为的堆大小),对于较小的实验,Arraycopy
仍然优于Stream
。 所以我认为当目的地的大小与使用arraycopy
不同时,没有更好的方法来复制数组。
答案 0 :(得分:1)
您也可以尝试衡量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
1}}。 (见copyBigger
例子)
对于copySmaller
示例,我们正在执行不同的操作:您正在将original
的前半部分复制到destination
,我正在复制大于length / 2
的元素,在我的情况下要求original
上的完整迭代。您将如何使用System.arraycopy
实现此目的?
对于SystemArraycopyBigger
,您只需将destination
数组大小设置为original
的两倍,但最后只复制size
。请注意,在我的StreamArraycopyBigger
destination
数组中有size ^ 2
个元素,而不是size * 2
:对于original
数组中的每个元素,我们有一个额外的{{1}元素数量。
结果最终可能不会有太大变化,但如果您想测试等效操作而不是比较苹果和橙子,请尝试这样做。
另外,为什么不尝试更大的样本量,例如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
可能会更快。