Arrays.copyOf: примеры (JAVA)

Копирование и масштабирование массивов в Java
Раздел: Массивы
Arrays.copyOf(int[] original, int newLength): int[]

Общее описание метода Arrays.copyOf

Метод Arrays.copyOf() из пакета java.util предоставляет удобный способ скопировать массив и одновременно изменить его длину. Выполняется поверхностное копирование элементов (shallow copy): для ссылочных типов копируются ссылки, а не сами объекты. При увеличении длины новые элементы инициализируются значениями по умолчанию: null для ссылочных типов и 0 / false для примитивов.

Доступные перегрузки (основные):

  • static T[] copyOf(T[] original, int newLength) - возвращает массив того же типа, что и original.
  • static T[] copyOf(U[] original, int newLength, Class newType) - создаёт массив заданного типа newType и копирует элементы, выполняя проверку совместимости типов при присваивании.
  • Перегрузки для примитивов: copyOf(byte[] , int), copyOf(int[] , int), copyOf(char[] , int) и т.д. для всех примитивных массивов.

Поведение по длине:

  • Если newLength == original.length, возвращается новый массив той же длины, содержащий копию элементов.
  • Если newLength < original.length, элементы с индексами ≥ newLength отбрасываются (обрезка).
  • Если newLength > original.length, в конец добавляются значения по умолчанию (null или 0/false).

Возвращаемое значение: новый массив указанной длины; исходный массив не изменяется.

Типичные исключения:

  • NullPointerException - если original равен null.
  • NegativeArraySizeException - если newLength отрицателен.
  • ArrayStoreException - при использовании перегрузки с newType, если элементы не могут быть размещены в массиве запрошенного типа.

Реализация обычно использует System.arraycopy, поэтому сложность копирования линейна по числу копируемых элементов. Метод удобен для быстрого изменения длины массивов и для приведения массивов к требуемому типу (при соблюдении совместимости типов).

Короткие примеры использования

1. Простое расширение и усечение примитивного массива:

import java.util.Arrays;
class Demo1 {
    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        int[] b = Arrays.copyOf(a, 5); // расширение
        int[] c = Arrays.copyOf(a, 2); // усечение
        System.out.println(Arrays.toString(b));
        System.out.println(Arrays.toString(c));
    }
}
[1, 2, 3, 0, 0]
[1, 2]

2. Для ссылочных типов добавляются null:

import java.util.Arrays;
class Demo2 {
    public static void main(String[] args) {
        Integer[] src = {10, 20};
        Integer[] dst = Arrays.copyOf(src, 4);
        System.out.println(Arrays.toString(dst));
    }
}
[10, 20, null, null]

3. Перегрузка с указанием типа массива (приведение типа массива):

import java.util.Arrays;
class Demo3 {
    public static void main(String[] args) {
        Object[] objs = {"a", "b"};
        String[] strs = Arrays.copyOf(objs, objs.length, String[].class);
        System.out.println(Arrays.toString(strs));
    }
}
[a, b]

4. Попытка преобразовать несовместимые элементы приведёт к исключению:

import java.util.Arrays;
class Demo4 {
    public static void main(String[] args) {
        Object[] mixed = {"a", 1};
        // следующая строка при выполнении вызовет ArrayStoreException
        String[] strs = Arrays.copyOf(mixed, mixed.length, String[].class);
    }
}
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
    at java.base/java.lang.System.arraycopy(Native Method)
    ...

5. Отрицательная длина вызывает исключение:

import java.util.Arrays;
class Demo5 { public static void main(String[] a) { int[] x = {1}; Arrays.copyOf(x, -1); } }
Exception in thread "main" java.lang.NegativeArraySizeException
    at java.base/java.util.Arrays.copyOf(Arrays.java:...)
    ...

Похожие методы в Java и особенности

  • Arrays.copyOfRange(original, from, to) - копирует указанный диапазон. Удобнее при извлечении подмассива.
  • System.arraycopy(src, srcPos, dest, destPos, length) - низкоуровневый, быстрый, даёт контроль позиций, но требует заранее созданного массива назначения.
  • array.clone() - создаёт копию всего массива того же типа и длины; не позволяет изменить длину прямо.
  • Коллекции: ArrayList и другие коллекции удобнее при динамическом изменении размера; массивы остаются предпочтительны для примитивов и при необходимости максимальной производительности.
  • Streams (например, Stream.toArray()) - удобны для преобразований, но могут быть медленнее и требуют дополнительных шагов для управления длиной и типом.

Рекомендуется выбирать:

  • Arrays.copyOf - при простом изменении длины или быстром приведении массива к виду нужного типа.
  • System.arraycopy - при копировании частей массивов с контролем смещений и высокой производительности.
  • ArrayList - при частых операциях вставки/удаления элементов.

Аналоги в других языках и их отличия

Краткие эквиваленты поведения (копирование + изменение длины):

  • JavaScript: slice() и concat(). Для массивов JS расширение добавлением undefined. Пример:
const a = [1,2,3];
const b = a.slice(0,2); // [1,2]
const c = a.concat([undefined, undefined]); // расширение до длины 5
b -> [1, 2]
c -> [1, 2, 3, undefined, undefined]
  • Python: срезы и операции с list. Удобно: lst[:n] для усечения, для расширения - добавление элементов или использование + [None]*(n-len).
a = [1,2,3]
b = a[:2]
c = a + [None]*(5-len(a))
print(b)
print(c)
[1, 2]
[1, 2, 3, None, None]
  • PHP: array_slice() для усечения, для расширения - можно присоединить элементы или использовать array_pad().
$a = [1,2,3];
$b = array_slice($a, 0, 2);
$c = array_pad($a, 5, null);
var_export($b);
var_export($c);
array ( 0 => 1, 1 => 2, )
array ( 0 => 1, 1 => 2, 2 => 3, 3 => NULL, 4 => NULL, )
  • C#: Array.Copy и Array.Resize(ref array, newSize). Array.Resize создаёт новый массив и присваивает ссылку переменной, поведение близко к Arrays.copyOf.
using System;
class X{ static void Main(){ int[] a = {1,2,3}; Array.Resize(ref a,5); Console.WriteLine(string.Join(", ", a));}}
1, 2, 3, 0, 0
  • Go: встроенная функция copy(dst, src) и создание слайса через make([]int, newLen). Для примитивов копируется значение, для ссылочных типов копируются ссылки.
package main
import ("fmt")
func main(){ a := []int{1,2,3}; b := make([]int,5); copy(b,a); fmt.Println(b) }
[1 2 3 0 0]
  • Kotlin: array.copyOf(newSize) (встроенная функция в Kotlin stdlib), поведение аналогично Java.
fun main(){ val a = arrayOf(1,2,3); val b = a.copyOf(5); println(b.joinToString()) }
1, 2, 3, null, null

Отличия от Java: многие языки используют динамические массивы/списки по умолчанию, поэтому операцию «копировать и изменить длину» чаще реализуют через встроенные операции со списками и срезами. Для статично типизированных языков (C#, Go) есть функции, близкие по семантике.

Типичные ошибки и их проявления

1. Ожидание глубокого копирования вместо поверхностного. Пример:

import java.util.Arrays;
class Err1 {
    public static void main(String[] args) {
        StringBuilder[] arr = {new StringBuilder("x")};
        StringBuilder[] copy = Arrays.copyOf(arr, 1);
        copy[0].append("y");
        System.out.println(arr[0]);
    }
}
xy

Пояснение: оба массива содержат ссылку на один и тот же объект.

2. Передача null в качестве original:

import java.util.Arrays;
class Err2 { public static void main(String[] args) { Arrays.copyOf(null, 3); } }
Exception in thread "main" java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:...) 
    ...

3. Отрицательная длина:

int[] a = {1};
Arrays.copyOf(a, -5);
Exception in thread "main" java.lang.NegativeArraySizeException
    at java.base/java.util.Arrays.copyOf(Arrays.java:...)
    ...

4. Ошибка приведения при использовании newType:

Object[] m = {"a", 2};
String[] s = Arrays.copyOf(m, m.length, String[].class);
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
    at java.base/java.lang.System.arraycopy(Native Method)
    ...

5. Ожидание автоматического расширения, как у коллекций. Arrays.copyOf создаёт новый массив, но не заменяет автоматически исходную ссылку, если это не сделать вручную.

Изменения и история метода

Методы Arrays.copyOf и Arrays.copyOfRange появились в Java SE 6 и сохраняют стабильный интерфейс в последующих версиях. Основные моменты:

  • Новых значительных перегрузок после введения в JDK 6 не добавлялось; перегрузки для примитивов были частью первоначального набора.
  • В более поздних релизах улучшений и оптимизаций реализации могло быть сделано, но семантика остаётся обратимо совместимой.

Для современных версий Java метод остаётся стандартным и рекомендуемым для быстрого изменения длины массива и создания копий.

Расширенные и нестандартные варианты применения

1. Реализация простого механизма динамического массива (похожего на ArrayList):

Пример java
import java.util.Arrays;
class DynInt {
    private int[] data = new int[2];
    private int size = 0;
    void add(int v){
        if (size == data.length) data = Arrays.copyOf(data, data.length * 2);
        data[size++] = v;
    }
    int get(int i){ return data[i]; }
    public String toString(){ return Arrays.toString(Arrays.copyOf(data, size)); }
    public static void main(String[] args){
        DynInt d = new DynInt();
        for(int i=0;i<5;i++) d.add(i);
        System.out.println(d);
    }
}
[0, 1, 2, 3, 4]

Пояснение: copyOf используется для удвоения ёмкости внутреннего массива при необходимости.

2. Копирование двумерного массива (внимание к поверхностному копированию):

Пример java
import java.util.Arrays;
class TwoD {
    public static void main(String[] args){
        int[][] a = {{1,2},{3,4}};
        int[][] b = Arrays.copyOf(a, a.length+1); // новый первый уровень, внутренние массивы те же
        System.out.println(Arrays.deepToString(b));
        b[0][0] = 9;
        System.out.println(Arrays.deepToString(a));
    }
}
[[1, 2], [3, 4], null]
[[9, 2], [3, 4]]

Пояснение: внутренние массивы не клонируются автоматически; изменение одного отражается в другом.

3. Приведение типа массива при гарантии совместимости элементов (удобно при работе с API, возвращающими Object[]):

Пример java
import java.util.Arrays;
class CastExample {
    public static void main(String[] args){
        Object[] objs = {"one","two"};
        // если точно известно, что все элементы String
        String[] s = Arrays.copyOf(objs, objs.length, String[].class);
        for(String t : s) System.out.println(t.toUpperCase());
    }
}
ONE
TWO

4. Создание массива заданного типа с расширением и автозаполнением дефолтными значениями (использование в рефлексии/обобщениях):

Пример java
import java.util.Arrays;
class ReflectCopy {
    public static void main(String[] args){
        Number[] nums = new Integer[] {1,2};
        // создаётся массив типа Number[]; элементы присваиваются, если совместимы
        Number[] n2 = Arrays.copyOf(nums, 4, Number[].class);
        System.out.println(Arrays.toString(n2));
    }
}
[1, 2, null, null]

5. Конкатенация массивов через копирование (эффективно для небольшого числа операций):

Пример java
import java.util.Arrays;
class Concat {
    static int[] concat(int[] a, int[] b){
        int[] r = Arrays.copyOf(a, a.length + b.length);
        System.arraycopy(b, 0, r, a.length, b.length);
        return r;
    }
    public static void main(String[] args){
        System.out.println(Arrays.toString(concat(new int[]{1,2}, new int[]{3,4})));
    }
}
[1, 2, 3, 4]

Заключение: Arrays.copyOf удобен в оптимизированных потоках кода, где требуется быстро скопировать и изменить размер массива без набора вспомогательных конструкций.

джава Arrays.copyOf function comments

En
Arrays.copyOf Копирует массив с возможным изменением длины