Что является более эффективным: System.arraycopy или Arrays.copyOf?
toArray
метод в ArrayList
Блох использует оба 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;
}
Как я могу сравнить эти два метода копирования и когда я должен использовать какие?
10 ответов
Разница в том, что 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;
}
В то время как 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. Это значительно медленнее, чем простая C-style memcopy содержимого массива.
Другое дело, что Arrays.copyOf
использования System.arraycopy
под капотом, таким образом, вы экономите, создавая новый массив и заполняя его самостоятельно, используя arraycopy
будет минимальным. Предполагая, что это то, что вы пытаетесь сделать...
Мой совет - использовать версию, которая делает ваш код проще для чтения, и беспокоиться только о том, какая из них быстрее, если профилирование говорит вам, что это важно.
1 - Это может быть быстрее, но также возможно, что JIT-компилятор делает такую хорошую работу по оптимизации цикла ручного кода, что нет никакой разницы.
Если вам нужна точная копия массива (скажем, если вы хотите сделать защитную копию), наиболее эффективным способом копирования массива, вероятно, является использование объекта массива clone()
метод:
class C {
private int[] arr;
public C(int[] values){
this.arr = values.clone();
}
}
Я не удосужился протестировать его производительность, но есть хороший шанс быть довольно быстрым, так как он все нативный (выделение и копирование в вызове), а клонирование - это своего рода особый благословенный способ копирования объектов JVM (и это в основном зло для других целей) и, скорее всего, сможет принять некоторые "ярлыки".
Лично я бы еще использовал clone
если это было медленнее, чем любой другой способ копирования, потому что его легче читать и почти невозможно испортить при записи. System.arrayCopy
, с другой стороны...
System.arrayCopy намного быстрее. Он находится в системе, потому что он использует прямую копию памяти за пределами земли Java. Используйте его, когда это возможно.
Вы смотрели на реализацию 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()
внутренне, так что производительность будет такой же.
System.arrayCopy
реализован изначально, и, следовательно, будет быстрее, чем любой код Java. Я рекомендую вам использовать его.
Вместо обсуждения, это реальные результаты. Очевидно, что ваш выбор будет зависеть от того, сколько данных вы хотите скопировать.
byte [] копировать тест производительности
10 000 000 итераций 40b array.copyOfRange: 135ms systems.arraycopy: 141ms
10 000 000 итераций 1000b array.copyOfRange: 1861ms systems.arraycopy: 2211ms
10 000 000 итераций 4000b array.copyOfRange: 6315ms systems.arraycopy: 5251ms
1,000,000 итераций 100,000b array.copyOfRange: 15,198ms systems.arraycopy: 14783ms
Я разместил это в другом ответе, но может быть и здесь.
Я знаю, что это ни в коем случае не определенно , поскольку тестирование таких операций - это отдельная наука, но просто для удовольствия я сделал несколько тестов для сравнения и.
Базовый массив - это
String[]
массив, заполненныйnulls
. Размер массива составляет от 10 миллионов до 100 миллионов элементов.Массив разбивается 24 раза.
Истекшее время показывает лучшее время из 3 разных запусков для каждого базового размера.
В любом случае, я не верю, что существует достаточная разница, чтобы гарантировать, что Arraycopy работает быстрее; Кроме того, чтобы быть полным, тест также должен включать различные размеры разделения (кроме 24), а также другие типы данных и массивы, заполненные значениями (кроме null).
Протестируйте его в Интернете (я тестировал его локально, но может быть полезно).
Тестовый код
Общий блок:
String[] a = new String[baseSize]; // f.e - 10.000.000
int size = baseSize / 24;
int rem = baseSize % 24;
И для каждого метода:
System.arraycopy
long start = System.currentTimeMillis();
String[][]pieces = new String[23][size];
String[] last = new String[size+rem];
for (int i=0;i<23;i++)
System.arraycopy(a, (size*i), pieces[i], 0 , size);
System.arraycopy(a, (size*23), last, 0 ,(size)+rem);
long elapsed = System.currentTimeMillis()-start;
Arrays.copyOfRange
long start = System.currentTimeMillis();
String[][] pieces = new String[23][];
for (int i=0;i<23;i++)
pieces[i] = Arrays.copyOfRange(a, size*i, size*(i+1));
String[] last = Arrays.copyOfRange(a, size*23, (size*24)+rem);
long elapsed = System.currentTimeMillis()-start;
Код легко настраивается, так что не стесняйтесь экспериментировать с общим количеством частей, на которые нужно разбить, типами данных, заполненными массивами и т. Д. Радоваться, веселиться!
Если вы посмотрите на исходный код 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 () для более чистого кода.
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));
}
}