Что является более эффективным: System.arraycopy или Arrays.copyOf? - PullRequest
70 голосов
/ 07 апреля 2010

Метод toArray в ArrayList, Bloch использует и System.arraycopy, и Arrays.copyOf для копирования массива.

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

Как можно сравнить эти два метода копирования и когда следует использоватькоторый?

Ответы [ 9 ]

97 голосов
/ 07 апреля 2010

Разница в том, что Arrays.copyOf не только копирует элементы, но и создает новый массив. System.arraycopy копирует в существующий массив.

Вот источник для Arrays.copyOf, поскольку вы можете видеть, что он использует System.arraycopy для заполнения нового массива:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}
32 голосов
/ 07 апреля 2010

Хотя System.arraycopy реализовано изначально и поэтому может быть 1 быстрее, чем цикл Java, это не всегда так быстро, как вы могли ожидать.Рассмотрим следующий пример:

Object[] foo = new Object[]{...};
String[] bar = new String[foo.length];

System.arraycopy(foo, 0, bar, 0, bar.length);

В этом случае базовый тип массивов foo и bar имеет разные базовые типы, поэтому реализация arraycopy должна проверять тип каждой ссылкископировать, чтобы убедиться, что это на самом деле ссылка на экземпляр String.Это значительно медленнее, чем простая memcopy в стиле C. содержимого массива.

Другой момент заключается в том, что Arrays.copyOf использует System.arraycopy под капотом.Поэтому System.arraycopy равно на первый взгляд не должно быть медленнее 2 , чем Arrays.copyOf.Но вы можете видеть (из кода выше ), что Arrays.copyOf в некоторых случаях будет использовать отражение для создания нового массива.Таким образом, сравнение производительности не является простым.

Есть несколько недостатков в этом анализе.

  1. Мы смотрим на код реализации из конкретной версии Java.Эти методы могут измениться, сделав недействительными предыдущие предположения об эффективности.

  2. Мы игнорируем возможность того, что JIT-компилятор может выполнить некоторую умную оптимизацию для этих методов.И, видимо, это происходит с Arrays.copyOf;см. Почему Arrays.copyOf в 2 раза быстрее, чем System.arraycopy для небольших массивов? .Этот метод является «встроенным» в реализации Java текущего поколения, что означает, что JIT-компилятор будет игнорировать то, что находится в исходном коде Java!

Но в любом случае разница между двумя версиями равно O(1) (т.е. не зависит от размера массива) и относительно мало.Поэтому я бы посоветовал использовать версию, которая делает ваш код проще для чтения, и беспокоиться только о том, какая из них быстрее, если profiling говорит вам, что это важно.


1 - может быть быстрее, но также возможно , что JIT-компилятор делает такую ​​хорошую работу по оптимизации цикла кода вручную, что нет никакой разницы.

13 голосов
/ 07 апреля 2010

Если вам нужна точная копия массива (скажем, если вы хотите сделать защитную копию), наиболее эффективный способ копирования массива, вероятно, заключается в использовании метода clone() объекта массива:

class C {
    private int[] arr;
    public C(int[] values){
        this.arr = values.clone();
    }
}

Я не удосужился протестировать его производительность, но у него есть хороший шанс быть довольно быстрым, поскольку он все нативный (выделение и копирование в вызове), а клонирование - это своего рода особый благословенный способ копирования объектов JVM (и в большинстве случаев это зло для других целей) и, скорее всего, сможет использовать некоторые «горячие клавиши».

Лично я все равно использовал бы clone, если бы он был медленнее, чем любой другой способ копирования, потому что его легче читать и почти невозможно испортить при записи. System.arrayCopy, с другой стороны ...

12 голосов
/ 07 апреля 2010

System.arrayCopy намного быстрее. Это в системе, потому что он использует прямую копию памяти за пределами земли Java. Используйте его, когда это возможно.

11 голосов
/ 07 апреля 2010

Вы смотрели на реализацию Arrays.copyOf () от Sun?

 public static int[] copyOf(int[] original, int newLength) {
    int[] copy = new int[newLength];
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

Как видно, он использует System.arraycopy() внутри, поэтому производительность будет такой же.

3 голосов
/ 07 апреля 2010

System.arrayCopy реализован изначально и, следовательно, будет быстрее, чем любой код Java. Я рекомендую вам использовать его.

1 голос
/ 27 декабря 2018

Если вы посмотрите на исходный код System.arraycopy () of и Array.copyOf (), для производительности.

System.arraycopy () - это код на C, он работает непосредственно с вашим массивом, не возвращает никаких значений и поэтому должен работать намного быстрее, чем Array.copyOf (). Тем не менее, если вам нужен новый массив для создания или если вам просто нужно значение операции копирования, вам нужно создать новый массив для этого, установить новую длину массива и т. Д. Таким образом, вы не можете сделать возврат System.arraycopy (источник, 0, пункт назначения, 0, длина).

Для того, что Array.copyOf () может сделать тогда, он создаст для вас новый массив. Вы можете присвоить возвращаемое значение из Array.copyOf () для массива или вернуть его из метода, так как Array.copyOf () вернет вам значение вместо того, чтобы работать непосредственно с целевым массивом. Таким образом, ваш код будет выглядеть намного чище. Тем не менее, с точки зрения производительности, Array.copyOf () - это метод универсального типа, и он не знает заранее, с чем он будет работать. Таким образом, он должен вызвать Array.newInstance () или new Object (), а затем привести его к типу массива ввода.

Итак, подведем итоги. Используйте System.arraycopy () из-за производительности. Используйте Array.copyOf () для более чистого кода.

0 голосов
/ 24 февраля 2018

Вместо обсуждения, это фактические результаты.Очевидно, ваш выбор будет зависеть от того, сколько данных вы хотите скопировать.

байт [] тест производительности копирования

10 000 000 итераций 40b array.copyOfRange: 135ms systems.arraycopy: 141 мс

10 000 000 итераций 1000b array.copyOfRange: 1861 мс system.arraycopy: 2211 мс

10 000 000 итераций 4000b array.copyOfRange: 6315 мс systems.arraycopy: 5251 мс

1 000 000 итераций 100 000 b array.copyOfRange: 15 198 мс system.arraycopy: 14783 мс

0 голосов
/ 23 октября 2013
      class ArrayCopyDemo {
   public static void main(String[] args) {
    char[] copyFrom = { 'd', 'e', 'c', 'a', 'f', 'f', 'e',
            'i', 'n', 'a', 't', 'e', 'd' };
    char[] copyTo = new char[7];

    System.arraycopy(copyFrom, 2, copyTo, 0, 7);
    System.out.println(new String(copyTo));
}
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...