Триває набір нової групи на курс Enterprise Patterns! Старт курсу 02.12.2024. Реєструйтеся зі знижкою 15% до 15.11.2024!
Дізнатися більше
30.10.2024
7 хвилин читання

Копіювання Масивів у Java: Кращі Практики і Приклади

Копіювання масивів є дуже поширеною операцією в програмуванні, і хоча ця задача може здаватися простою на перший погляд, насправді існує кілька підходів до її виконання. 

Поверхневе клонування — це процес копіювання об’єкта, при якому створюється новий об’єкт, але його поля містять посилання на ті ж самі об’єкти, що і поля оригінального об’єкта. Це може призвести до неочікуваних наслідків, якщо оригінальний масив змінюється після клонування, адже зміни будуть відображатися і в його копії.

На відміну від поверхневого, глибоке клонування передбачає створення незалежних копій усіх об’єктів, на які посилаються поля оригінального об’єкта. Це означає, що зміни в одному об’єкті не вплинуть на інший. 

Ви дізнаєтесь, як правильно використовувати ці підходи на практиці, і отримаєте конкретні приклади коду, які допоможуть вам краще зрозуміти цей процес.

Бажаєте освоїти професію Java Developer? Приєднуйтесь до програми “Від 0 до Strong Java Junior за 12 місяців”. Скористайтесь вигідною пропозицією від FoxmindEd!
Зареєструватись

Поверхневе клонування

Метод System.arraycopy()

Метод System.arraycopy() є потужним і ефективним інструментом для копіювання масивів у Java. Цей метод належить до класу System і дозволяє швидко копіювати елементи одного масиву в інший, що може бути особливо корисно при роботі з великими масивами або у випадках, коли потрібна максимальна продуктивність. Проте System.arraycopy() не дозволяє змінювати або фільтрувати елементи масиву під час копіювання. 

Метод System.arraycopy() має наступний синтаксис:

System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

Де:

  • src — базовий масив, з якого потрібно скопіювати елементи.
  • srcPos — позиція у базовому масиві (індекс), з якого потрібно почати копіювати, якщо вказати 2, то копіювання почнеться з 3ого елементу масиву.
  • dest — масив, у який потрібно скопіювати елементи.
  • destPos — позиція у новому масиві (індекс), з якого почнеться вставка елемнентів.
  • length — кількість елементів, які потрібно скопіювати.
public class Main {

   public static void main(String[] args) {

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

       int[] copiedArray = new int[originalArray.length];

       System.arraycopy(originalArray, 0, copiedArray, 0, originalArray.length); -> {1, 2, 3, 4, 5}

   }

}

Давайте перевіримо, чи дійсно метод не здійснює глибокого копіювання: 

public class Main {

   public static void main(String[] args) {

       // Створюємо масив об'єктів StringBuilder, бо StringBuilder, навідміну від String, є mutable

       StringBuilder[] originalArray = {

               new StringBuilder("Apple"),

               new StringBuilder("Banana"),

               new StringBuilder("Cherry")

       };

       StringBuilder[] copiedArray = new StringBuilder[originalArray.length];

       // Копіюємо елементи з оригінального масиву в новий масив

       System.arraycopy(originalArray, 0, copiedArray, 0, originalArray.length);

       // copiedArray містить елементи {"Apple", "Banana", "Cherry"}

       // Змінюємо один з елементів в оригінальному масиві

       originalArray[0].append(" Pie");

       // тепер copiedArray містить елементи {"Apple Pie", "Banana", "Cherry"}, тобто перший елемент зазнав змін

   }

}

Метод Arrays.copyOf()

Метод, що дуже схожий на попередній, проте належить до класу Arrays. Дозволяє легко створити копію масиву, визначивши новий розмір копії, якщо це потрібно. 

Метод Arrays.copyOf() має наступний синтаксис:

T[] copyOf(T[] original, int newLength);

Де:

  • original — масив, який потрібно скопіювати.
  • newLength — новий розмір масиву. Якщо newLength більший за довжину оригінального масиву, нові елементи в кінці будуть ініціалізовані значеннями за замовчуванням для типу масиву (наприклад, 0 для числових масивів, null для об’єктів).
public class Main {

   public static void main(String[] args) {

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

       int[] copiedArray = Arrays.copyOf(originalArray, originalArray.length);

   }

}

Метод Arrays.copyOfRange()

Метод Arrays.copyOfRange() є розширенням попереднього методу і надає можливість скопіювати частину масиву, вказавши діапазон індексів, який вас цікавить. Цей метод корисний, коли потрібно скопіювати лише певний відрізок масиву, а не весь масив повністю.

Метод Arrays.copyOfRange() має наступний синтаксис:

T[] copyOfRange(T[] original, int from, int to);

Де:

  • original — масив, з якого потрібно зробити копію.
  • from — початковий індекс (включно), з якого починається копіювання.
  • to — кінцевий індекс (невключно), до якого триває копіювання.
public class Main {

   public static void main(String[] args) {

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

       int[] copiedArray = Arrays.copyOfRange(originalArray, 1, 4);

   }

}

У цьому прикладі новий масив copiedArray буде містити елементи з originalArray від індексу 1 до індексу 3 (значення 2, 3 і 4), тобто {2, 3, 4}. 

Будьте уважні, якщо to значно перевищує розмір базового масиву, новий масив може містити багато елементів, ініціалізованих значеннями за замовчуванням, що може призвести до зайвого використання пам’яті.

Метод clone()

Кожен масив є об’єктом, який успадковує метод clone() від класу Object. Метод clone() дозволяє створити новий масив, який є точною копією вихідного масиву. Проте, ви не можете легко змінити розмір нового масиву або скопіювати лише частину елементів, чи елементи за певною умовою.

public class Main {

   public static void main(String[] args) {

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

       int[] clonedArray = originalArray.clone();

   }

}

Цикл

Цикл for є одним із найпростіших і найпоширеніших способів копіювання масиву або деяких його елементів у Java. Цей підхід дозволяє вручну перебрати всі елементи масиву та скопіювати їх у новий масив. Він надає повний контроль над процесом копіювання, за рахунок чого ви можете фільтрувати елементи, змінювати значення елементів чи скопіювати лише частину масиву. Проте для великих масивів цей метод може бути менш ефективним, оскільки він виконує перебір кожного елемента. 

public class Main {

   public static void main(String[] args) {

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

       int[] copiedArray = new int[originalArray.length];

       for (int i = 0; i < originalArray.length; i++) {

           // можна додати if-else блок якщо треба скопіювати елементи за певною умовою або змінити певні елементи

           copiedArray[i] = originalArray[i];

       }

   }

}

Глибоке копіювання

Щоб досягти глибого копіювання нам необхідно не лише клонувати кожен рядок масиву, але і створювати нові екземпляри для кожного об’єкта у цьому масиві. Таким чином, будь-які зміни в оригінальному масиві не вплинуть на копію, оскільки кожен об’єкт у новому масиві буде незалежним. 

Цикл 

Використання циклу дає нам можливість здійснювати ручне управління при копіюванні масиву. Тому ми можемо зробити глибоке копіювання елементів і уникати небажаних побічних ефектів коли старий масивів змінюється. 

public class Main {

   public static void main(String[] args) {

       StringBuilder[] originalArray = {

               new StringBuilder("Apple"),

               new StringBuilder("Banana"),

               new StringBuilder("Cherry"),

               new StringBuilder("Date")

       };

       // Створюємо новий масив для глибокого копіювання

       StringBuilder[] deepCopy = new StringBuilder[originalArray.length];

       // Копіюємо елементи з оригінального масиву в новий масив

       for (int i = 0; i < originalArray.length; i++) {

           // Створюємо новий екземпляр StringBuilder для кожного елемента

           deepCopy[i] = new StringBuilder(originalArray[i].toString());

       }

   }

}

 Бібліотека Apache Commons Lang

Бібліотека Apache Commons Lang пропонує клас SerializationUtils, який можна використовувати для глибокого копіювання об’єктів через серіалізацію. Цей метод працює для будь-яких об’єктів, які реалізують інтерфейс Serializable.

public class Main {

   public static void main(String[] args) {

       // Використовуємо String замість StringBuilder для серіалізації

       String[] originalArray = {"Apple", "Banana", "Cherry", "Date"};

       // Використання SerializationUtils для глибокого копіювання

       String[] deepCopy = SerializationUtils.clone(originalArray);

   }

}
Підпишіться на наш Ютуб-канал! Корисні відео для програмістів чекають на вас! YouTube
Оберіть свій курс програмування! Шлях до кар’єри програміста починається тут! Подивитись

Метод clone()

Так, метод clone() може здійснювати глибоке копіювання, але для цього необхідно, щоб об’єкти реалізували інтерфейс Cloneable. 

class CustomObject implements Cloneable {

   private StringBuilder data;

   public CustomObject(StringBuilder data) {

       this.data = data;

   }

   public StringBuilder getData() {

       return data;

   }

   @Override

   protected CustomObject clone() {

       return new CustomObject(new StringBuilder(data));

   }

}

public class Main {

   public static void main(String[] args) {

       // Створюємо оригінальний об'єкт

       CustomObject originalObject = new CustomObject(new StringBuilder("Original Data"));

       // Копіюємо об'єкт за допомогою методу clone()

       CustomObject clonedObject = originalObject.clone();

       // Вносимо зміни до оригінального об'єкта

       originalObject.getData().append(" - modified");

       // Виводимо дані оригінального та клонованого об'єктів

       System.out.println("Original Object: " + originalObject.getData()); // -> Original Object: Original Data - modified

       System.out.println("Cloned Object: " + clonedObject.getData()); // -> Cloned Object: Original Data

   }

}

Висновок

Ми розглянули кілька різних підходів до копіювання масивів у Java, кожен з яких має свої переваги та обмеження. Вибір підходу залежить від конкретної ситуації і вимог вашої задачі.

Цикл надає найбільший контроль над процесом копіювання. Він дозволяє не тільки копіювати елементи масиву, але й фільтрувати їх, змінювати під час копіювання або вибірково копіювати лише частину масиву. Також задопомогою циклу ви можете здійснювати глибоке або поверхневе копіювання. Такий підхід підходить у випадках, коли вам потрібно більше гнучкості або коли ви хочете реалізувати специфічні операції над елементами масиву. Однак, цей метод може бути зайвим, якщо вам просто потрібно створити копію масиву без додаткових маніпуляцій.

Методи System.arraycopy(), Arrays.copyOf(), і Arrays.copyOfRange() є більш зручними та швидкими для копіювання масивів, коли вам не потрібно змінювати або фільтрувати дані. Вони забезпечують ефективне копіювання, що особливо корисно для великих масивів або коли важлива продуктивність. Проте, ці методи здійснюють лише поверхневе копіювання, що може бути достатнім для більшості ситуацій, але не підходить, якщо потрібно повне (глибоке) копіювання.

Глибоке копіювання, яке ми реалізували через цикли або інші підходи, необхідне тільки тоді, коли потрібно створити повністю незалежну копію масиву, включаючи всі підмасиви або об’єкти, на які він посилається. Це важливо в ситуаціях, коли зміни в оригінальному масиві не повинні впливати на його копію. Однак, завжди використовувати глибоке копіювання немає сенсу, оскільки воно є більш ресурсозатратним і складним, ніж поверхневе копіювання.

FAQ
Що таке поверхневе клонування в Java?

Поверхневе клонування створює новий масив, що містить посилання на ті ж об'єкти, що й оригінальний, тому зміни в оригіналі впливають на копію.

Чим відрізняється глибоке клонування?

Глибоке клонування створює повністю незалежні копії всіх об'єктів у масиві, тому зміни в оригіналі не впливають на копію.

Як використовувати метод System.arraycopy()?

System.arraycopy() швидко копіює елементи з одного масиву в інший, але забезпечує тільки поверхневе копіювання.

Що робить метод Arrays.copyOfRange()?

Метод Arrays.copyOfRange() копіює лише певну частину масиву, використовуючи діапазон індексів, який можна задати.

Чи можна здійснити глибоке копіювання через цикл?

Так, цикл for дозволяє контролювати копіювання елементів і створювати незалежні копії, якщо це потрібно для глибокого клонування.

Яка роль бібліотеки Apache Commons Lang для копіювання?

Apache Commons Lang має клас SerializationUtils, що дозволяє глибоке копіювання через серіалізацію об'єктів, які підтримують Serializable.

🤔 Залишилися запитання про копіювання масиву у Java? - Сміливо задавайте нижче! 💬

Додати коментар

Ваш імейл не буде опубліковано. Обов'язкові поля відзначені *

Зберегти моє ім'я, імейл та адресу сайту у цьому браузері для майбутніх коментарів