目标数组大小

时间:2017-09-19 08:02:29

标签: java arrays copy clone

在上一个问题中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.arraycopyStream接口的性能。下面我提供基准代码及其结果:

@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 不同时,没有更好的方法来复制数组。

1 个答案:

答案 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}元素数量。

结果最终可能不会有太大变化,但如果您想测试等效操作而不是比较苹果和橙子,请尝试这样做。

另外,为什么不尝试更大的样本量,例如size10_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可能会更快。