Сделать копию массива


345

У меня есть массив, aкоторый постоянно обновляется. Давайте скажем a = [1,2,3,4,5]. Мне нужно сделать точную копию aи позвонить b. Если aбы измениться [6,7,8,9,10], bвсе равно должно быть [1,2,3,4,5]. Каков наилучший способ сделать это? Я попробовал forцикл как:

for(int i=0; i<5; i++) {
    b[i]=a[i]
}

но это, кажется, не работает правильно. Пожалуйста, не используйте расширенные термины, такие как глубокий текст и т. Д., Потому что я не знаю, что это значит.

Ответы:


559

Вы можете попробовать использовать System.arraycopy ()

int[] src  = new int[]{1,2,3,4,5};
int[] dest = new int[5];

System.arraycopy( src, 0, dest, 0, src.length );

Но, вероятно, лучше использовать clone () в большинстве случаев:

int[] src = ...
int[] dest = src.clone();

9
+1 за не возвращение колеса. И, насколько я знаю, это решение быстрее, чем вы можете получить при копировании массива.
Фелипе Хаммел

6
оба клона и arraycopy являются родными. Я ожидаю, что клон будет немного быстрее. не то, что разница имеет значение.
MeBigFatGuy

5
@Felipe, @MeBigFatGuy - только для большого массива. Для небольшого массива цикл копирования может быть быстрее из-за накладных расходов на установку. Если вы посмотрите на javadoc System.arraycopy, то увидите, что метод должен проверять различные вещи перед его запуском. Некоторые из этих проверок не нужны в цикле копирования, в зависимости от типа статического массива.
Стивен К

7
@FelipeHummel, @MeBigFatGuy, @StephenC - Вот тест производительности методов копирования массива, упомянутых в ответах здесь. В этой установке clone()оказывается самым быстрым для 250 000 элементов.
Адам

6
Вызывает разочарование тот факт, что все обсуждения здесь касаются проблем с микропроцессором, которые в 99,999% случаев не имеют значения. Более важным моментом является то, что src.clone()он более читабелен и имеет гораздо меньше возможностей для ошибок, чем выделение нового массива и выполнение arraycopy. (А также бывает быстрым.)
Брайан Гетц

231

ты можешь использовать

int[] a = new int[]{1,2,3,4,5};
int[] b = a.clone();

также.


6
Я просто проясняю точку зрения ОП: « Если А изменится на [6,7,8,9,10], то В все равно будет [1,2,3,4,5] ». ОП сказал, что он пытался использовать петлю, но у него ничего не вышло.
Гарри Джой

15
В ролях нет необходимости; хороший статический анализатор предупредит об этом. Но клонирование, безусловно, лучший способ сделать новую копию массива.
Эриксон

5
@MeBigFatGuy - сценарий использования OP влечет за собой повторное копирование в один и тот же массив, поэтому клон не работает.
Стивен К

4
@ Стефан С, я этого не читал. Я только что прочитал, что он хочет получить копию, а затем будет неоднократно обновлять несохраненную версию.
MeBigFatGuy

4
@MeBigFatGuy - он сказал: «У меня есть массив A, который постоянно обновляется». , Может быть, я читаю слишком много в этом, но я понимаю, что это подразумевает, что он также постоянно копирует А в Б.
Стивен К

184

Если вы хотите сделать копию:

int[] a = {1,2,3,4,5};

Это путь:

int[] b = Arrays.copyOf(a, a.length);

Arrays.copyOfможет быть быстрее, чем a.clone()на небольших массивах. Оба элемента копирования одинаково быстро, но clone () возвращает, Objectпоэтому компилятор должен вставить неявное приведение к int[]. Вы можете увидеть это в байт-коде, что-то вроде этого:

ALOAD 1
INVOKEVIRTUAL [I.clone ()Ljava/lang/Object;
CHECKCAST [I
ASTORE 2

63

Хорошее объяснение от http://www.journaldev.com/753/how-to-copy-arrays-in-java

Методы копирования массива Java

Object.clone () : класс Object предоставляет метод clone (), и поскольку массив в java также является объектом, вы можете использовать этот метод для получения полной копии массива. Этот метод не подойдет вам, если вы хотите частичное копирование массива.

System.arraycopy () : Системный класс arraycopy () - лучший способ сделать частичное копирование массива. Он предоставляет вам простой способ указать общее количество элементов для копирования и позиции индекса исходного и конечного массивов. Например, System.arraycopy (source, 3, destination, 2, 5) скопирует 5 элементов из источника в место назначения, начиная с 3-го индекса источника до 2-го индекса назначения.

Arrays.copyOf (): если вы хотите скопировать первые несколько элементов массива или полную копию массива, вы можете использовать этот метод. Очевидно, что он не универсален, как System.arraycopy (), но он также не сбивает с толку и прост в использовании.

Arrays.copyOfRange () : если вы хотите скопировать несколько элементов массива, где начальный индекс не равен 0, вы можете использовать этот метод для копирования частичного массива.


35

У меня есть ощущение, что все эти «лучшие способы скопировать массив» на самом деле не решат вашу проблему.

Ты говоришь

Я пробовал цикл for, как [...], но, похоже, он работает неправильно?

Глядя на этот цикл, нет очевидной причины, по которой он не работает ... если только:

  • Вы каким - то образом имеют aи bмассивы перепутались (например , aи bотносятся к одному массиву), или
  • Ваше приложение является многопоточным, и разные потоки одновременно читают и обновляют aмассив.

В любом случае, альтернативные способы копирования не решат основную проблему.

Исправление для первого сценария очевидно. Для второго сценария вам нужно будет найти способ синхронизации потоков. Классы атомарных массивов не помогают, потому что у них нет конструкторов атомарных копий или методов клонирования, но синхронизация с использованием примитивного мьютекса поможет.

(В вашем вопросе есть подсказки, которые заставляют меня думать, что это действительно связано с ветками; например, ваше утверждение aпостоянно меняется.)


2
согласился .. наверное правда.
MeBigFatGuy


9

Все решения, которые вызывают длину из массива, добавляют ваш пример кода с избыточным значением null checkersconr:

int[] a = {1,2,3,4,5};
int[] b = Arrays.copyOf(a, a.length);
int[] c = a.clone();

//What if array a comes as local parameter? You need to use null check:

public void someMethod(int[] a) {
    if (a!=null) {
        int[] b = Arrays.copyOf(a, a.length);
        int[] c = a.clone();
    }
}

Я рекомендую вам не изобретать колесо и использовать служебный класс, где все необходимые проверки уже выполнены. Рассмотрим ArrayUtils из Apache Commons. Ваш код становится короче:

public void someMethod(int[] a) {
    int[] b = ArrayUtils.clone(a);
}

Apache Commons вы можете найти там


8

Вы также можете использовать Arrays.copyOfRange.

Пример :

public static void main(String[] args) {
    int[] a = {1,2,3};
    int[] b = Arrays.copyOfRange(a, 0, a.length);
    a[0] = 5;
    System.out.println(Arrays.toString(a)); // [5,2,3]
    System.out.println(Arrays.toString(b)); // [1,2,3]
}

Этот метод похож Arrays.copyOf, но он более гибкий. Оба они используют System.arraycopyпод капотом.

Смотрите :


3

Для нулевой безопасной копии массива вы также можете использовать опциональный Object.clone()метод, предоставленный в этом ответе .

int[] arrayToCopy = {1, 2, 3};
int[] copiedArray = Optional.ofNullable(arrayToCopy).map(int[]::clone).orElse(null);

Несмотря на то, что это решение слишком сложное, оно также приводит к потере памяти и, если массив содержит секрет (например, байтовый массив с паролем), оно также вводит недостатки безопасности, потому что промежуточные объекты будут находиться в куче до тех пор, пока сборка мусора не будет открыта. злоумышленникам.
Weltraumschaf

1
Я не согласен, что массив будет в куче специально для этой конструкции. На самом деле он вызывает клон только тогда, когда это необходимо, и Optionalобъект является просто пустым объектом со ссылкой на существующий массив. Что касается влияния на производительность, я бы сказал, что преждевременно говорить, что это на самом деле воздействие, поскольку этот тип конструкции является хорошим кандидатом для встраивания внутри JVM и, следовательно, не более, чем другие методы. Это вопрос стиля (функциональное программирование или процедурное программирование, но не только), рассматривать его как более сложный или нет.
Николя Хено

3

Если вы должны работать с сырыми массивами , а не ArrayListто Arraysесть то , что вам нужно. Если вы посмотрите на исходный код, это самый лучший способ получить копию массива. У них есть немного защитного программирования, потому чтоSystem.arraycopy() метод генерирует множество непроверенных исключений, если вы передаете ему нелогичные параметры.

Вы можете использовать любой из Arrays.copyOf()них, который будет копировать с первого Nthэлемента в новый более короткий массив.

public static <T> T[] copyOf(T[] original, int newLength)

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

2770
2771    public static <T,U> T[] More ...copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
2772        T[] copy = ((Object)newType == (Object)Object[].class)
2773            ? (T[]) new Object[newLength]
2774            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
2775        System.arraycopy(original, 0, copy, 0,
2776                         Math.min(original.length, newLength));
2777        return copy;
2778    }

или Arrays.copyOfRange()тоже сделает свое дело:

public static <T> T[] copyOfRange(T[] original, int from, int to)

Копирует указанный диапазон указанного массива в новый массив. Начальный индекс диапазона (от) должен лежать между нулем и оригинальной длиной, включительно. Значение в оригинале [из] помещается в начальный элемент копии (если не из == original.length или из == в). Значения из последующих элементов в исходном массиве помещаются в последующие элементы в копии. Конечный индекс диапазона (до), который должен быть больше или равен от, может быть больше, чем original.length, и в этом случае значение null помещается во все элементы копии, индекс которых больше или равен оригиналу. длина - от. Длина возвращаемого массива будет до - от. Полученный массив имеет тот же класс, что и исходный массив.

3035    public static <T,U> T[] More ...copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
3036        int newLength = to - from;
3037        if (newLength < 0)
3038            throw new IllegalArgumentException(from + " > " + to);
3039        T[] copy = ((Object)newType == (Object)Object[].class)
3040            ? (T[]) new Object[newLength]
3041            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
3042        System.arraycopy(original, from, copy, 0,
3043                         Math.min(original.length - from, newLength));
3044        return copy;
3045    }

Как видите, обе эти функции - просто обертки System.arraycopyс защитной логикой, что то, что вы пытаетесь сделать, является допустимым.

System.arraycopy это самый быстрый способ копирования массивов.


0

У меня была похожая проблема с 2D-массивами, и она закончилась. Я копировал основной массив и изменял значения внутренних массивов, и был удивлен, когда значения изменились в обеих копиях. В основном обе копии были независимыми, но содержали ссылки на одни и те же внутренние массивы, и мне пришлось сделать массив копий внутренних массивов, чтобы получить то, что я хотел.

Вероятно, это не проблема ОП, но я надеюсь, что она все еще может быть полезной.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.