Copy: примеры (JAVA)
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) Глубокое копирование объекта через сериализацию
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 (эффективность и нулевое копирование на уровне ОС)
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)
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()
ByteBuffer buf = ByteBuffer.allocate(10);
buf.put((byte)1);
ByteBuffer dup = buf.duplicate();
dup.flip();
System.out.println(dup.get());
1
Комментарий: duplicate создает новый буфер, разделяющий подложный массив/память; это не копия данных, а представление. Для независимого содержимого требуется копирование массива.
5) Копирующий конструктор как предпочтительная практика для сложных объектов
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 (пример схемы)
// схема: разбить большой массив на чанки, запустить несколько потоков, каждый делает System.arraycopy
// итог: более высокая пропускная способность на многоядерных системах для больших данных
// результат зависит от окружения и объема данных; потенциально быстрее, но требует синхронизации работы
Эти примеры показывают, что в Java доступны как простые утилиты для быстрой копии, так и мощные механизмы для контролируемого глубокого копирования или высокопроизводительного переноса больших объёмов данных.