Copy: примеры (JAVA)

Примеры copy-операций в Java
Раздел: Ввод-вывод (I/O) файловый
copy(InputStream in, OutputStream out): long

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

В Java под копированием понимаются разные операции: дублирование массивов, списков, потоков данных и объектов. Под именем copy чаще всего встречаются следующие API:

  • java.nio.file.Files.copy - копирование файлов или запись входного потока в файл.
  • System.arraycopy - быстрое системное копирование участков массивов (примитивы и объекты).
  • java.util.Arrays.copyOf / copyOfRange - удобное создание нового массива на основе существующего.
  • java.util.Collections.copy - копирование элементов из одного списка в другой.
  • Object.clone() - механизм поверхностного клонирования объектов (при поддержке интерфейса Cloneable и правильной реализации).
  • FileChannel.transferTo / transferFrom - эффективный перенос данных между файловыми каналами (включая возможности нулевого копирования на уровне ОС).

Кратко о сигнатурах и аргументах:

  • Files.copy(Path source, Path target, CopyOption... options)
    Аргументы: исходный и целевой пути, опции (например, StandardCopyOption.REPLACE_EXISTING, COPY_ATTRIBUTES).
    Возвращает: Path - путь к целевому файлу. Может бросать IOException, FileAlreadyExistsException, SecurityException.
  • Files.copy(InputStream in, Path target, CopyOption... options)
    Аргументы: входной поток, целевой путь, опции.
    Возвращает: long - число скопированных байт. Бросает IOException.
  • System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
    Аргументы: источник, смещение в источнике, назначение, смещение в назначении, длина в элементах.
    Возвращает: ничего (void). Возможные исключения: ArrayIndexOutOfBoundsException, ArrayStoreException, NullPointerException, IndexOutOfBoundsException.
  • Arrays.copyOf(T[] original, int newLength) / Arrays.copyOfRange
    Аргументы: исходный массив и новая длина (или диапазон). Возвращает новый массив того же типа. При newLength меньше исходного данные обрезаются, при большем - добавляются значения по умолчанию.
  • Collections.copy(List<? super T> dest, List<? extends T> src)
    Аргументы: список-назначение и список-источник. Возвращает void. Требование: dest.size() >= src.size(), иначе бросается IndexOutOfBoundsException. Копирование происходит по позициям (set), существующие элементы в dest перезаписываются.
  • Object.clone()
    Аргументы: нет (метод экземпляра). Возвращает Object - поверхностную копию объекта. Для корректной работы класс должен реализовать Cloneable и переопределить clone(). Возможные исключения: CloneNotSupportedException.
  • FileChannel.transferTo(long position, long count, WritableByteChannel target)
    Аргументы: позиция, количество байт, целевой канал. Возвращает long - число реально переданных байт. Используется для больших файлов, часто эффективнее потокового копирования.

Выбор конкретного метода зависит от задачи: для массивов и примитивов предпочтение System.arraycopy или Arrays.copyOf; для списков - Collections.copy или создание нового списка через конструктор; для файлов - Files.copy или FileChannel.transferTo; для объектов - реализованные копирующие конструкторы, паттерн прототипа (clone) либо глубокое копирование через сериализацию/ручной код.

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

Несколько простых кейсов с кодом и ожидаемым выводом.

1) System.arraycopy для примитивов

int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[7];
System.arraycopy(src, 1, dest, 2, 3);
System.out.println(Arrays.toString(dest));
[0, 0, 2, 3, 4, 0, 0]

2) Arrays.copyOf и copyOfRange

String[] a = {"a","b","c"};
String[] b = Arrays.copyOf(a, 5);
String[] c = Arrays.copyOfRange(a, 1, 3);
System.out.println(Arrays.toString(b));
System.out.println(Arrays.toString(c));
[a, b, c, null, null]
[b, c]

3) Collections.copy (требуется заранее подготовленный dest)

List<Integer> src = Arrays.asList(1,2,3);
List<Integer> dest = new ArrayList<>(Arrays.asList(0,0,0));
Collections.copy(dest, src);
System.out.println(dest);
[1, 2, 3]

4) Files.copy: запись InputStream в файл (пример вывода количества байт)

try (InputStream in = new ByteArrayInputStream("hello".getBytes())) {
    long copied = Files.copy(in, Paths.get("out.txt"), StandardCopyOption.REPLACE_EXISTING);
    System.out.println(copied + " bytes");
}
5 bytes

5) System.arraycopy с объектными массивами и ArrayStoreException (пример ошибки)

Object[] src = new Integer[]{1,2};
String[] dest = new String[2];
System.arraycopy(src, 0, dest, 0, 2);
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer

Похожие средства Java и их особенности

  • Arrays.copyOf / copyOfRange - удобны для получения нового массива; автоматически создают массив нужного типа и длины, но всегда выделяют память.
  • System.arraycopy - быстрый низкоуровневый способ, особенно для примитивных типов; не создает новый массив автоматически, требует подготовленного назначения.
  • Collections.copy - специализировано для списка; требует, чтобы список назначения уже имел достаточный размер, копирует через set, не меняет размер dest.
  • Object.clone() - устоявшийся, но спорный механизм; делает поверхностную копию по умолчанию, требует осторожности при вложенных изменяемых полях. Часто заменяется копирующим конструктором или фабрикой.
  • FileChannel.transferTo / transferFrom - предпочтительны при копировании больших файлов для повышения производительности и уменьшения числа копирований в памяти.

Выбор: для простого расширения массива - Arrays.copyOf; для высокопроизводительных побайтовых операций с файлами - FileChannel.transferTo; для копирования данных между списками - Collections.copy или создание new ArrayList(src).

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

Короткие примеры и особенности по языкам:

PHP - функция copy

$src = 'a.txt';
$dst = 'b.txt';
$result = copy($src, $dst);
var_dump($result);
bool(true)

Отличие: файловая копия на уровне ОС, возвращает bool, нет строгой типизации массивов/объектов как в Java.

JavaScript - структурное клонирование и методы массивов

let src = [1,2,3];
let copy = src.slice();
console.log(copy);
// для глубокого клонирования: structuredClone(obj) в современных средах
[1, 2, 3]

Python - модуль copy и срезы

import copy
l = [1, [2,3]]
sh = l.copy()   # поверхностно
deep = copy.deepcopy(l)
print(sh)
print(deep)
[1, [2, 3]]
[1, [2, 3]]

Отличие: deepcopy для рекурсивного копирования, простые срезы работают для списков.

SQL - INSERT ... SELECT

INSERT INTO target_table (col1, col2)
SELECT col1, col2 FROM source_table;
-- вставлены строки из source_table в target_table

C# - Array.Copy, Buffer.BlockCopy, File.Copy

int[] a = {1,2,3};
int[] b = new int[5];
Array.Copy(a, 0, b, 1, 3);
Console.WriteLine(string.Join(",", b));
0,1,2,3,0

Отличие: API похожи по возможностям; File.Copy - аналог Files.copy; Buffer.BlockCopy эффективен для примитивов.

Lua - ручное копирование таблиц (нет встроенного универсального copy)

function shallow_copy(t)
  local t2 = {}
  for k,v in pairs(t) do t2[k] = v end
  return t2
end
print(shallow_copy({1,2})[1])
1

Go (Golang) - встроенная функция copy

src := []int{1,2,3}
dst := make([]int, 5)
n := copy(dst, src)
fmt.Println(dst, n)
[1 2 3 0 0] 3

Отличие: copy возвращает число реально скопированных элементов, работает с срезами, автоматически учитывает длину.

Kotlin - стандартные функции и дата-классы

val src = arrayOf(1,2,3)
val copy = src.copyOf(5)
println(copy.joinToString())
1, 2, 3, 0, 0

Отличие: синтаксически близко к Java (JVM), есть удобные функции для data class: copy() создает измененную копию объекта.

Типичные ошибки при копировании

  • Неправильный размер назначения при Collections.copy: dest должен иметь размер не меньше источника. Пример:
List<Integer> src = Arrays.asList(1,2,3);
List<Integer> dest = new ArrayList<>(); // пустой
Collections.copy(dest, src);
Exception in thread "main" java.lang.IndexOutOfBoundsException: Source does not fit in dest
  • Несоответствие типов при System.arraycopy: ArrayStoreException при попытке положить элемент несовместимого класса.
Object[] src = new Integer[]{1};
String[] dest = new String[1];
System.arraycopy(src,0,dest,0,1);
java.lang.ArrayStoreException: java.lang.Integer
  • CloneNotSupportedException при использовании Object.clone без реализации Cloneable и/или без переопределения clone().
class A {}
A a = new A();
A b = (A) a.clone();
java.lang.CloneNotSupportedException: A
  • При Files.copy возможны FileAlreadyExistsException (если файл существует и не указан REPLACE_EXISTING), SecurityException или IOException при проблемах с доступом к файловой системе.

Рекомендации: проверять предварительно размеры/типы, использовать try-with-resources при работе с потоками, обрабатывать специфичные исключения или использовать более безопасные высокоуровневые методы.

Изменения и примечания по версиям Java

В последних релизах Java не было радикальных изменений для базовых API copy, однако появились и развились сопутствующие возможности:

  • InputStream.transferTo добавлен в Java 9; полезен для быстрого переноса данных между потоками без ручного буфера.
  • java.nio.file API (Files, Path, FileChannel) с течением версий получили улучшения в стабильности и поддержку дополнительных CopyOption: StandardCopyOption.REPLACE_EXISTING, COPY_ATTRIBUTES и др.
  • Параллельно развивались средства сериализации и JSON-библиотеки, которые стали часто использоваться для глубокого копирования объектов вместо устаревшего clone().

В целом рекомендация экосистемы: отдавать предпочтение явно контролируемым копирующим конструкторам, фабрикам или утилитам библиотек вместо слепого использования clone.

Расширенные и редкие сценарии копирования

Несколько продвинутых примеров с кодом и комментариями.

1) Глубокое копирование объекта через сериализацию

Пример java
static <T extends Serializable> T deepCopy(T obj) throws Exception {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(baos)) {
        oos.writeObject(obj);
        try (ObjectInputStream ois = new ObjectInputStream(
                 new ByteArrayInputStream(baos.toByteArray()))) {
            return (T) ois.readObject();
        }
    }
}

// использование
List<List<Integer>> orig = new ArrayList<>(Arrays.asList(new ArrayList<>(Arrays.asList(1))));
List<List<Integer>> copy = deepCopy((Serializable) orig);
copy.get(0).set(0, 9);
System.out.println(orig.get(0).get(0));
System.out.println(copy.get(0).get(0));
1
9

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

2) Копирование больших файлов с FileChannel.transferTo (эффективность и нулевое копирование на уровне ОС)

Пример java
try (FileChannel src = FileChannel.open(srcPath, StandardOpenOption.READ);
     FileChannel dst = FileChannel.open(dstPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
    long size = src.size();
    long pos = 0;
    while (pos < size) {
        pos += src.transferTo(pos, Math.min(size - pos, 8 * 1024 * 1024), dst);
    }
}
System.out.println("done");
done

Комментарий: разбивка на чанки помогает избежать ограничений платформы.

3) Копия списка с преобразованием (map + collect)

Пример java
List<String> src = Arrays.asList("a","b");
List<Integer> converted = src.stream().map(String::length).collect(Collectors.toList());
System.out.println(converted);
[1, 1]

Комментарий: полезно, когда требуется не просто копия, а преобразование элементов при копировании.

4) Копирование буфера без выделения данных: ByteBuffer.duplicate()

Пример java
ByteBuffer buf = ByteBuffer.allocate(10);
buf.put((byte)1);
ByteBuffer dup = buf.duplicate();
dup.flip();
System.out.println(dup.get());
1

Комментарий: duplicate создает новый буфер, разделяющий подложный массив/память; это не копия данных, а представление. Для независимого содержимого требуется копирование массива.

5) Копирующий конструктор как предпочтительная практика для сложных объектов

Пример java
class Person {
  String name;
  List<String> tags;
  Person(Person other) {
    this.name = other.name;
    this.tags = new ArrayList<>(other.tags); // глубокая копия коллекции
  }
}

Person p1 = new Person();
// инициализация p1...
Person p2 = new Person(p1);
// p2 независим от p1 по коллекциям

Комментарий: копирующие конструкторы дают явный контроль над степенью копирования (поверхностное vs глубокое).

6) Параллельное копирование частей больших массивов с System.arraycopy (пример схемы)

Пример java
// схема: разбить большой массив на чанки, запустить несколько потоков, каждый делает System.arraycopy
// итог: более высокая пропускная способность на многоядерных системах для больших данных
// результат зависит от окружения и объема данных; потенциально быстрее, но требует синхронизации работы

Эти примеры показывают, что в Java доступны как простые утилиты для быстрой копии, так и мощные механизмы для контролируемого глубокого копирования или высокопроизводительного переноса больших объёмов данных.

джава copy function comments

En
Copy Copies all bytes from an input stream to an output stream