java - 当目标数组大小不同时 System.arraycopy 的替代方法

标签 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方法?

编辑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 更好的复制数组的方法了。 .

最佳答案

您可以尝试根据 java8Streams 来衡量性能同样,当您需要过滤掉或向 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_0001_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/

相关文章:

java - 从边构造树

java - 什么时候不要在 Java 中使用 static 关键字?

java - 为什么 Maven 需要相同版本的不同依赖项?

python - IO错误: [Errno 13] Permission denied: I do have permissions?

java - Tomcat 5.5 : . jar 未在 .war 重建时发布

c++ - 在删除数组之前如何检查以确保已分配数组

python - 如何比较两个具有 NaN 值的 numpy 数组?

PHP 从 URL 中获取 CSV,加载到数组中,格式化

javascript - Gulp 不会覆盖 JS 文件

vim - 粘贴多次