为什么clone()是复制数组的最佳方法?

时间:2017-09-10 21:40:18

标签: java arrays copy clone

这对我来说是一种耻辱,但我不知道:

  

你应该使用clone来复制数组,因为那通常是   最快的方法。

正如Josh Bloch在本博客中所述:http://www.artima.com/intv/bloch13.html

我总是使用System.arraycopy(...)。 这两种方法都是原生的,所以可能没有深入到我无法弄清楚的库的来源,为什么会如此。

我的问题很简单:  为什么它是最快的方式? System.arraycopy有什么区别? 区别在于here,但它没有回答为什么Josh Bloch认为clone()是最快的方式的问题。

6 个答案:

答案 0 :(得分:20)

我想谈谈为什么clone()是复制数组的最快方法,而不是System.arraycopy(..)或其他人:

1。 clone()在将源数组复制到目标服务器here之前,不必进行类型检查。它只是简单地分配新的内存空间并将对象分配给它。另一方面,System.arraycopy(..)检查类型,然后复制数组。

2。 clone()也会中断优化以消除冗余归零。如您所知,Java中每个已分配的数组都必须使用0s或相应的默认值进行初始化。但是,如果JIT在创建后立即填充数组,则可以避免将该数组归零。与使用现有0s或相应的默认值更改副本值相比,这确实更快。使用System.arraycopy(..)花费大量时间清除和复制已初始化的数组。为此,我进行了一些基准测试。

@BenchmarkMode(Mode.Throughput)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 10, time = 1, batchSize = 1000)
@Measurement(iterations = 10, time = 1, batchSize = 1000)
public class BenchmarkTests {

    @Param({"1000","100","10","5", "1"})
    private int size;
    private int[] original;

    @Setup
    public void setup() {
        original = new int[size];
        for (int i = 0; i < size; i++) {
            original[i] = i;
        }
    }

    @Benchmark
    public int[] SystemArrayCopy() {
        final int length = size;
        int[] destination = new int[length];
        System.arraycopy(original, 0, destination, 0, length);
        return destination;
    }


    @Benchmark
    public int[] arrayClone() {
        return original.clone();
    }

}

<强>输出:

Benchmark                        (size)   Mode  Cnt       Score      Error  Units
ArrayCopy.SystemArrayCopy            1  thrpt   10   26324.251 ± 1532.265  ops/s
ArrayCopy.SystemArrayCopy            5  thrpt   10   26435.562 ± 2537.114  ops/s
ArrayCopy.SystemArrayCopy           10  thrpt   10   27262.200 ± 2145.334  ops/s
ArrayCopy.SystemArrayCopy          100  thrpt   10   10524.117 ±  474.325  ops/s
ArrayCopy.SystemArrayCopy         1000  thrpt   10     984.213 ±  121.934  ops/s
ArrayCopy.arrayClone                 1  thrpt   10   55832.672 ± 4521.112  ops/s
ArrayCopy.arrayClone                 5  thrpt   10   48174.496 ± 2728.928  ops/s
ArrayCopy.arrayClone                10  thrpt   10   46267.482 ± 4641.747  ops/s
ArrayCopy.arrayClone               100  thrpt   10   19837.480 ±  364.156  ops/s
ArrayCopy.arrayClone              1000  thrpt   10    1841.145 ±  110.322  ops/s

根据我得到的输出clone几乎快两倍System.arraycopy(..)

3。此外,使用像clone()这样的手动复制方法会导致更快的输出,因为它不必进行任何VM调用(与System.arraycopy()不同)。

答案 1 :(得分:4)

首先,clone()不必进行System.arraycopy()进行的类型检查。

答案 2 :(得分:3)

我想纠正并补充以前的答案。

  1. Object.clone对数组使用未经检查的System.arraycopy实现;
  2. Object.clone的主要性能改进,它是直接初始化RAW内存。在System.arraycopy的情况下,它也尝试将数组初始化与复制操作结合起来,正如我们在源代码中看到的那样,但它也会对此进行不同的额外检查,这与Object.clone不同。如果您只是禁用此功能(见下文),那么性能将非常接近(特别是在我的硬件上)。
  3. 另一个有趣的事情是关于Young vs Old Gen。如果源阵列对齐并且在Old Gen内部,两种方法都有接近的性能。
  4. 当我们复制原始数组时,System.arraycopy总是使用generate_unchecked_arraycopy。
  5. 这取决于硬件/操作系统相关的实现,所以不要相信基准和假设,请自行检查。
  6. <强>解释

    首先,克隆方法和System.arraycopy是内在函数。 Object.clone和System.arraycopy使用generate_unchecked_arraycopy。 如果我们更深入,我们可以看到在HotSpot之后选择具体实现,依赖于操作系统等

    朗力。 我们来看看Hotspot中的代码。 首先,我们将看到Object.clone(LibraryCallKit :: inline_native_clone)使用generate_arraycopy,在 -XX:-ReduceInitialCardMarks 的情况下,它用于System.arraycopy。否则它会执行LibraryCallKit :: copy_to_clone,它会在RAW内存中初始化新数组(如果-XX:+ ReduceBulkZeroing,默认情况下启用)。 相比之下,System.arraycopy直接使用generate_arraycopy,尝试检查ReduceBulkZeroing(以及许多其他情况)并消除数组归零,并提及额外的检查,并且还会进行额外的检查以确保所有元素都已初始化,这与Object.clone不同。最后,在最好的情况下,它们都使用generate_unchecked_arraycopy。

    下面我展示一些基准测试,看看这种效果对实践的影响:

    1. 第一个是简单的基准测试,与以前的答案唯一的区别是,数组没有排序;我们看到arraycopy较慢(但不是两次),结果 - https://pastebin.com/ny56Ag1z;
    2. 其次,我添加选项-XX:-ReduceBulkZeroing,现在我看到两种方法的性能非常接近。结果 - https://pastebin.com/ZDAeQWwx;
    3. 我还假设我们将在Old / Young之间有区别,因为数组对齐(这是Java GC的一个特性,当我们调用GC时,数组的对齐方式发生了变化,很容易使用{{3 }})。令我感到惊讶的是,这两种方法的性能变得一致,并且降级。结果 - JOL。对于相信具体数字的人,System.arraycopy的吞吐量优于Object.clone。
    4. 第一个基准:

      import org.openjdk.jmh.annotations.*;
      import org.openjdk.jmh.runner.Runner;
      import org.openjdk.jmh.runner.options.OptionsBuilder;
      
      import java.util.concurrent.ThreadLocalRandom;
      import java.util.concurrent.TimeUnit;
      
      @State(Scope.Benchmark)
      @BenchmarkMode(Mode.All)
      @OutputTimeUnit(TimeUnit.MILLISECONDS)
      public class CloneVsArraycopy {
      
          @Param({"10", "1000", "100000"})
          int size;
      
          int[] source;
      
          @Setup(Level.Invocation)
          public void setup() {
              source = create(size);
          }
      
          @Benchmark
          public int[] clone(CloneVsArraycopy cloneVsArraycopy) {
              return cloneVsArraycopy.source.clone();
          }
      
          @Benchmark
          public int[] arraycopy(CloneVsArraycopy cloneVsArraycopy) {
              int[] dest = new int[cloneVsArraycopy.size];
              System.arraycopy(cloneVsArraycopy.source, 0, dest, 0, dest.length);
              return dest;
          }
      
          public static void main(String[] args) throws Exception {
              new Runner(new OptionsBuilder()
                      .include(CloneVsArraycopy.class.getSimpleName())
                      .warmupIterations(20)
                      .measurementIterations(20)
                      .forks(20)
                      .build()).run();
          }
      
          private static int[] create(int size) {
              int[] a = new int[size];
              for (int i = 0; i < a.length; i++) {
                  a[i] = ThreadLocalRandom.current().nextInt();
              }
              return a;
          }
      
      }
      

      在我的电脑上运行此测试,我得到了这个 - https://pastebin.com/bTt5SJ8r。 差异不是很大,但仍然存在。

      第二个基准测试我只添加了一个设置 -XX:-ReduceBulkZeroing 并得到了这个结果https://pastebin.com/ny56Ag1z。不,我们看到Young Gen的差异也不大。

      在第三个基准测试中,我只更改了设置方法并启用了ReduceBulkZeroing选项:

      @Setup(Level.Invocation)
      public void setup() {
          source = create(size);
          // try to move to old gen/align array
          for (int i = 0; i < 10; ++i) {
              System.gc();
          }
      }
      

      差异要小得多(可能是错误间隔) - https://pastebin.com/ZDAeQWwx

      声明

      这也可能是错的。你应该自己检查一下。

      另外

      我认为,查看基准测试过程很有意思:

      # Benchmark: org.egorlitvinenko.arrays.CloneVsArraycopy.arraycopy
      # Parameters: (size = 50000)
      
      # Run progress: 0,00% complete, ETA 00:07:30
      # Fork: 1 of 5
      # Warmup Iteration   1: 8,870 ops/ms
      # Warmup Iteration   2: 10,912 ops/ms
      # Warmup Iteration   3: 16,417 ops/ms <- Hooray!
      # Warmup Iteration   4: 17,924 ops/ms <- Hooray!
      # Warmup Iteration   5: 17,321 ops/ms <- Hooray!
      # Warmup Iteration   6: 16,628 ops/ms <- What!
      # Warmup Iteration   7: 14,286 ops/ms <- No, stop, why!
      # Warmup Iteration   8: 13,928 ops/ms <- Are you kidding me?
      # Warmup Iteration   9: 13,337 ops/ms <- pff
      # Warmup Iteration  10: 13,499 ops/ms
      Iteration   1: 13,873 ops/ms
      Iteration   2: 16,177 ops/ms
      Iteration   3: 14,265 ops/ms
      Iteration   4: 13,338 ops/ms
      Iteration   5: 15,496 ops/ms
      

      For Object.clone

      # Benchmark: org.egorlitvinenko.arrays.CloneVsArraycopy.clone
      # Parameters: (size = 50000)
      
      # Run progress: 0,00% complete, ETA 00:03:45
      # Fork: 1 of 5
      # Warmup Iteration   1: 8,761 ops/ms
      # Warmup Iteration   2: 12,673 ops/ms
      # Warmup Iteration   3: 20,008 ops/ms
      # Warmup Iteration   4: 20,340 ops/ms
      # Warmup Iteration   5: 20,112 ops/ms
      # Warmup Iteration   6: 20,061 ops/ms
      # Warmup Iteration   7: 19,492 ops/ms
      # Warmup Iteration   8: 18,862 ops/ms
      # Warmup Iteration   9: 19,562 ops/ms
      # Warmup Iteration  10: 18,786 ops/ms
      

      我们可以在此观察System.arraycopy的性能降级。我看到Streams的相似图片,编译器中有一个错误。 我想它也可能是编译器中的错误。无论如何,经过3次热身表演降级后很奇怪。

      <强>更新

      什么是类型检查

      import org.openjdk.jmh.annotations.*;
      import org.openjdk.jmh.runner.Runner;
      import org.openjdk.jmh.runner.options.OptionsBuilder;
      
      import java.util.concurrent.ThreadLocalRandom;
      import java.util.concurrent.TimeUnit;
      import java.util.concurrent.atomic.AtomicLong;
      
      @State(Scope.Benchmark)
      @BenchmarkMode(Mode.All)
      @OutputTimeUnit(TimeUnit.MILLISECONDS)
      public class CloneVsArraycopyObject {
      
          @Param({"100"})
          int size;
      
          AtomicLong[] source;
      
          @Setup(Level.Invocation)
          public void setup() {
              source = create(size);
          }
      
          @Benchmark
          @CompilerControl(CompilerControl.Mode.DONT_INLINE)
          public AtomicLong[] clone(CloneVsArraycopyObject cloneVsArraycopy) {
              return cloneVsArraycopy.source.clone();
          }
      
          @Benchmark
          @CompilerControl(CompilerControl.Mode.DONT_INLINE)
          public AtomicLong[] arraycopy(CloneVsArraycopyObject cloneVsArraycopy) {
              AtomicLong[] dest = new AtomicLong[cloneVsArraycopy.size];
              System.arraycopy(cloneVsArraycopy.source, 0, dest, 0, dest.length);
              return dest;
          }
      
          public static void main(String[] args) throws Exception {
              new Runner(new OptionsBuilder()
                      .include(CloneVsArraycopyObject.class.getSimpleName())
                      .jvmArgs("-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining", "-XX:-ReduceBulkZeroing")
                      .warmupIterations(10)
                      .measurementIterations(5)
                      .forks(5)
                      .build())
                      .run();
          }
      
          private static AtomicLong[] create(int size) {
              AtomicLong[] a = new AtomicLong[size];
              for (int i = 0; i < a.length; i++) {
                  a[i] = new AtomicLong(ThreadLocalRandom.current().nextLong());
              }
              return a;
          }
      
      }
      

      未观察到差异 - https://pastebin.com/bTt5SJ8r。 我想解释很简单,因为在这种情况下System.arraycopy是热内在的,真正的实现只是内联而没有任何类型转换等。

      注意

      我同意Radiodef您会发现有趣的阅读https://pastebin.com/ufxCZVaC,此博客的作者是blog post的创作者(或创作者之一)。

答案 3 :(得分:1)

就副本而言System.arrayCopy是最快的,然后现在。

  • System.arrayCopy不会创建新数组,也无法以原始复制速度进行打击。
  • Arrays.copyOf只需创建一个数组并调用arrayCopy。便利性。
  • Array.clone效率很高,但需要将复制的数据刷新到所有cpu缓存。

如果您可以使用arrayCopy重用数组的方式进行编码,那就去吧。 否则,我个人建议copyOf给出cpu核心的上升趋势,因为克隆通常是considered oldproblematic - {{3}的主要观点那个问题开始了。

  

与常见的想法相反,实际的复制循环(类型是否已选中)是 Java字节码,并且热点无法优化。   循环用C ++编写,是低级jvm实现。

答案很长:

  

这个答案是基于和链接到OpenJDK 8的源代码,据我所知,对于Sun来说应该是相同的。

数组副本可能比大多数人想象的要复杂得多。在C代码级别,它可以分为三种情况:

  1. 原始数组为Josh Bloch blogcopied directly
  2. 同一类的对象数组或超类数组的子类也是copy loop
  3. 否则,在不同类的数组之间,会对copied directly进行类型检查。
  4. 复制数组的绝对速度因此在很大程度上取决于数组类型。 但是,三种克隆方法的相对速度并不是因为它们都解析为相同的复制循环,内联的C ++或汇编循环。 因此,速度的不同主要是由间接费用和其他因素造成的。

    • each element 本质上是类型检查和长度检查,然后直接进行复制循环。 在我自己的测试中,arrayCopy总是比其他两种方法更快,超出任何误差范围。

    • System.arrayCopy 只需在创建新数组后调用System.arrayCopy - 。 请注意,调用Array.clone。 与Radiodef的Arrays.copyOf相反,没有迹象表明Java 8将绕过零初始化。

    • comment 很有意思。 它使用最少的检查直接调用堆分配和复制循环。 因此,它的数组创建速度应该比Arrays.copyOf快,如果速度不快,它的副本速度应该快System.arrayCopy

    但在我的测试中,Array.clone略慢于copyOf

    我怀疑这是因为复制后的Array.clone。 与构造函数一样,clone将确保复制的数据对所有线程都可见 - System.arrayCopyArray.copyOf都不会。 这意味着Array.clone需要花时间等待CPU缓存更新。

    如果这是真的,Array.clone vs Arrays.copyOf的结果取决于clone的缓存刷新是否比copyOf的开销更快,并且应该是平台相关的

    除此之外,由于克隆总是会产生相同类型的数组,因此所有三种方法最终都使用相同的复制循环。

    如果您只想复制,arrayCopy总是最快,只是因为它不会创建新数组。 对于其他人来说,如果java邮件列表是可以使用的,Arrays.copyOfArray.clone之间的选择应该主要是memory barrier

    我的jmh测试结果和下面的代码。

    • 单向测试返回复制的数组。
    • 双向测试覆盖复制源,强制下一个克隆复制“新”数据。
    • NoClone不会克隆任何东西,并且是确保更高速度更快的标准。
      

    如上所述,Clone和CopyOf是一场紧密的竞赛,您的里程可能会有所不同。

    /* # Run complete. Total time: 00:06:44
    
    Benchmark                               Mode  Cnt          Score         Error  Units
    MyBenchmark.ArrayCloneByteOneWay       thrpt   20    1048588.503 ±    2608.862  ops/s
    MyBenchmark.ArrayCloneByteTwoWay       thrpt   20     523782.848 ±    1613.823  ops/s
    MyBenchmark.ArrayCloneObjOneWay        thrpt   20     260903.006 ±    1311.827  ops/s
    MyBenchmark.ArrayCloneObjTwoWay        thrpt   20     129448.639 ±    1179.122  ops/s
    MyBenchmark.ArraysCopyOfByteOneWay     thrpt   20    1065995.804 ±    2197.919  ops/s
    MyBenchmark.ArraysCopyOfByteTwoWay     thrpt   20     533025.610 ±    2831.955  ops/s
    MyBenchmark.ArraysCopyOfObjOneWay      thrpt   20     266134.565 ±    1536.756  ops/s
    MyBenchmark.ArraysCopyOfObjTwoWay      thrpt   20     130821.380 ±     274.325  ops/s
    MyBenchmark.NoClone                    thrpt   20  308776528.157 ± 2546848.128  ops/s
    MyBenchmark.SystemArrayCopyByteOneWay  thrpt   20    1232733.367 ±    8439.409  ops/s
    MyBenchmark.SystemArrayCopyByteTwoWay  thrpt   20     859387.983 ±    1919.359  ops/s
    MyBenchmark.SystemArrayCopyObjOneWay   thrpt   20     239532.442 ±     775.193  ops/s
    MyBenchmark.SystemArrayCopyObjTwoWay   thrpt   20     167235.661 ±     503.141  ops/s
    */
    
    import java.util.Arrays;
    import java.util.Random;
    import org.openjdk.jmh.annotations.*;
    
    @Fork(2) @Warmup(iterations = 5, time = 1) @Measurement(iterations = 10, time = 1)
    public class Q46230557 {
       private static final int ARRAY_SIZE = 8192;
    
       @State(Scope.Thread) public static class Data {
          public byte[] bytes = new byte[ ARRAY_SIZE ];
          public Object[] objs = new Object[ ARRAY_SIZE ];
          @Setup public void setup() {
             final Random RNG = new Random();
             RNG.nextBytes( bytes );
             for ( int i = 0 ; i < ARRAY_SIZE ; i++ )
                objs[i] = RNG.nextInt();
          }
       }
    
       @Benchmark public byte[] NoClone( final Data data ) {
          return data.bytes;
       }
    
       @Benchmark public byte[] SystemArrayCopyByteOneWay( final Data data ) {
          final byte[] dest = new byte[ ARRAY_SIZE ];
          System.arraycopy( data.bytes, 0, dest, 0, ARRAY_SIZE );
          return dest;
       }
    
       @Benchmark public byte[] SystemArrayCopyByteTwoWay( final Data data ) {
          final byte[] buf = new byte[ ARRAY_SIZE ];
          System.arraycopy( data.bytes, 0, buf, 0, ARRAY_SIZE );
          System.arraycopy( buf, 0, data.bytes, 0, ARRAY_SIZE );
          return data.bytes;
       }
    
       @Benchmark public byte[] ArraysCopyOfByteOneWay( final Data data ) {
          return Arrays.copyOf( data.bytes, ARRAY_SIZE );
       }
    
       @Benchmark public byte[] ArraysCopyOfByteTwoWay( final Data data ) {
          final byte[] buf = Arrays.copyOf( data.bytes, ARRAY_SIZE );
          return data.bytes = Arrays.copyOf( buf, ARRAY_SIZE );
       }
    
       @Benchmark public byte[] ArrayCloneByteOneWay( final Data data ) {
          return data.bytes.clone();
       }
    
       @Benchmark public byte[] ArrayCloneByteTwoWay( final Data data ) {
          final byte[] buf = data.bytes.clone();
          return data.bytes = buf.clone();
       }
    
       @Benchmark public Object[] SystemArrayCopyObjOneWay( final Data data ) {
          final Object[] dest = new Object[ ARRAY_SIZE ];
          System.arraycopy( data.objs, 0, dest, 0, ARRAY_SIZE );
          return dest;
       }
    
       @Benchmark public Object[] SystemArrayCopyObjTwoWay( final Data data ) {
          final Object[] buf = new Object[ ARRAY_SIZE ];
          System.arraycopy( data.objs, 0, buf, 0, ARRAY_SIZE );
          System.arraycopy( buf, 0, data.objs, 0, ARRAY_SIZE );
          return data.objs;
       }
    
       @Benchmark public Object[] ArraysCopyOfObjOneWay( final Data data ) {
          return Arrays.copyOf( data.objs, ARRAY_SIZE );
       }
    
       @Benchmark public Object[] ArraysCopyOfObjTwoWay( final Data data ) {
          final Object[] buf = Arrays.copyOf( data.objs, ARRAY_SIZE );
          return data.objs = Arrays.copyOf( buf, ARRAY_SIZE );
       }
    
       @Benchmark public Object[] ArrayCloneObjOneWay( final Data data ) {
          return data.objs.clone();
       }
    
       @Benchmark public Object[] ArrayCloneObjTwoWay( final Data data ) {
          final Object[] buf = data.objs.clone();
          return data.objs = buf.clone();
       }
    }
    

答案 4 :(得分:0)

性能上的差异来自于跳过数组被清零的步骤。

public static int[] copyUsingArraycopy(int[] original)
{
    // Memory is allocated and zeroed out
    int[] copy = new int[original.Length];
    // Memory is copied
    System.arraycopy(original, 0, copy, 0, original.length);
}

public static int[] copyUsingClone(int[] original)
{
    // Memory is allocated, but not zeroed out
    // Unitialized memory is then copied into
    return (int[])original.clone();
}

但是,如果复制阵列的性能有显着差异,通常最好采用双缓冲。

int[] backBuffer = new int[BUFFER_SIZE];
int[] frontBuffer = new int[BUFFER_SIZE];

...

// Swap buffers
int[] temp = frontBuffer;
frontBuffer = backBuffer;
backBuffer = temp;
System.arraycopy(frontBuffer, 0, backBuffer, 0, BUFFER_SIZE);

答案 5 :(得分:0)

不太同意拖延者的答案。我不知道您使用哪个jdk启动了jmh测试,但结果却不一样。

对我来说, System.arraycopy Clone()快。

@BenchmarkMode(Mode.Throughput)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 10, time = 1, batchSize = 1000)
@Measurement(iterations = 10, time = 1, batchSize = 1000)
public class ArrayCopyTest {

  @Param({"1000","100","10","5", "1"})
  private int size;
  private int[] original;
  private int[] dest;

  @Setup
  public void setup() {
    original = new int[size];
    for (int i = 0; i < size; i++) {
      original[i] = i;
    }
    dest = new int[size];
  }

  @Benchmark
  public int[] SystemArrayCopy() {
    final int length = size;
    int[] destination = new int[length];
    System.arraycopy(original, 0, destination, 0, length);
    return destination;
  }

  @Benchmark
  public int[] SystemArrayCopyCache() {
    System.arraycopy(original, 0, dest, 0, original.length);
    return dest;
  }

  @Benchmark
  public int[] arrayClone() {
    return original.clone();
  }

  public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
        .include(ArrayCopyTest.class.getSimpleName())
        .build();

    new Runner(opt).run();
  }
}

然后使用jdk8.1.0_121

获得结果
Benchmark                               (size)   Mode  Cnt         Score        Error  Units
ops/s
ArrayCopyTest.SystemArrayCopy             1000  thrpt   10      1332,640 ±     79,860  ops/s
ArrayCopyTest.SystemArrayCopy              100  thrpt   10     11850,158 ±    617,639  ops/s
ArrayCopyTest.SystemArrayCopy               10  thrpt   10     50440,946 ±   1409,152  ops/s
ArrayCopyTest.SystemArrayCopy                5  thrpt   10     68791,250 ±   1538,610  ops/s
ArrayCopyTest.SystemArrayCopy                1  thrpt   10     95913,164 ±    671,765  ops/s
ArrayCopyTest.SystemArrayCopyCache        1000  thrpt   10     13514,812 ±    211,703  ops/s
ArrayCopyTest.SystemArrayCopyCache         100  thrpt   10     74976,673 ±   2026,528  ops/s
ArrayCopyTest.SystemArrayCopyCache          10  thrpt   10    108410,738 ±    576,100  ops/s
ArrayCopyTest.SystemArrayCopyCache           5  thrpt   10    118921,286 ±   1354,365  ops/s
ArrayCopyTest.SystemArrayCopyCache           1  thrpt   10    141092,949 ±   2872,961  ops/s
ArrayCopyTest.arrayClone                  1000  thrpt   10      1030,526 ±     40,950  ops/s
ArrayCopyTest.arrayClone                   100  thrpt   10      5233,746 ±    163,820  ops/s
ArrayCopyTest.arrayClone                    10  thrpt   10      8556,687 ±     77,213  ops/s
ArrayCopyTest.arrayClone                     5  thrpt   10      8895,238 ±    241,374  ops/s
ArrayCopyTest.arrayClone                     1  thrpt   10      9036,695 ±    243,890  ops/s

令人惊讶的是,arraycopy比克隆更好。我建议使用它,它在大循环算法中也非常有用,可以重用对象并避免垃圾收集器带来的压力。使用arraycopy,您可以重复使用阵列,避免浪费时间/取消分配时间