วิธีคัดลอก Array ใน Java

1. ภาพรวม

ในบทความสั้น ๆ นี้เราจะพูดถึงวิธีการคัดลอกอาร์เรย์ต่างๆใน Java การคัดลอกอาร์เรย์อาจดูเหมือนเป็นงานเล็กน้อย แต่อาจทำให้เกิดผลลัพธ์ที่ไม่คาดคิดและพฤติกรรมของโปรแกรมหากไม่ทำอย่างรอบคอบ

2. คลาสของระบบ

เริ่มต้นด้วยไลบรารี Java หลัก - System.arrayCopy () ; สิ่งนี้จะคัดลอกอาร์เรย์จากอาร์เรย์ต้นทางไปยังอาร์เรย์ปลายทางโดยเริ่มต้นการคัดลอกจากตำแหน่งต้นทางไปยังตำแหน่งเป้าหมายจนถึงความยาวที่ระบุ

จำนวนองค์ประกอบที่คัดลอกไปยังอาร์เรย์เป้าหมายเท่ากับความยาวที่ระบุ เป็นวิธีง่ายๆในการคัดลอกลำดับย่อยของอาร์เรย์ไปยังอีก

ถ้ามีข้อโต้แย้งอาร์เรย์เป็นโมฆะมันพ่นNullPointerExceptionและถ้าใด ๆ ของอาร์กิวเมนต์ที่เป็นจำนวนเต็มเป็นลบหรือออกจากช่วงก็พ่นIndexOutOfBoundException

มาดูตัวอย่างการคัดลอกอาร์เรย์แบบเต็มไปยังอาร์เรย์อื่นโดยใช้คลาสjava.util.System :

int[] array = {23, 43, 55}; int[] copiedArray = new int[3]; System.arraycopy(array, 0, copiedArray, 0, 3);

อาร์กิวเมนต์ใช้วิธีนี้ ได้แก่ อาร์เรย์ต้นทางตำแหน่งเริ่มต้นที่จะคัดลอกจากอาร์เรย์ต้นทางอาร์เรย์ปลายทางตำแหน่งเริ่มต้นในอาร์เรย์ปลายทางและจำนวนองค์ประกอบที่จะคัดลอก

มาดูตัวอย่างอื่นที่แสดงการคัดลอกลำดับย่อยจากอาร์เรย์ต้นทางไปยังปลายทาง:

int[] array = {23, 43, 55, 12, 65, 88, 92}; int[] copiedArray = new int[3]; System.arraycopy(array, 2, copiedArray, 0, 3); 
assertTrue(3 == copiedArray.length); assertTrue(copiedArray[0] == array[2]); assertTrue(copiedArray[1] == array[3]); assertTrue(copiedArray[2] == array[4]); 

3. คลาสอาร์เรย์

อาร์เรย์ชั้นนอกจากนี้ยังมีวิธีการมากเกินไปหลายคัดลอกอาร์เรย์ไปยังอีก ภายในจะใช้แนวทางเดียวกันกับคลาสSystemที่เราได้เห็นก่อนหน้านี้ ส่วนใหญ่จะมีสองวิธีcopyOf ( ... )และcopyRangeOf ( ... )

มาดูที่copyOf กันก่อน:

int[] array = {23, 43, 55, 12}; int newLength = array.length; int[] copiedArray = Arrays.copyOf(array, newLength); 

สิ่งสำคัญคือต้องสังเกตว่าคลาสArraysใช้Math.min (…)สำหรับการเลือกค่าต่ำสุดของความยาวอาร์เรย์ต้นทางและค่าของพารามิเตอร์ความยาวใหม่เพื่อกำหนดขนาดของอาร์เรย์ผลลัพธ์

Arrays.copyOfRange ()รับพารามิเตอร์ 2 ตัวคือ ' from'และ ' to'นอกเหนือจากพารามิเตอร์อาร์เรย์ต้นทาง อาร์เรย์ผลลัพธ์มีดัชนี'จาก'แต่ไม่รวมดัชนี'ถึง' ลองดูตัวอย่าง:

int[] array = {23, 43, 55, 12, 65, 88, 92}; int[] copiedArray = Arrays.copyOfRange(array, 1, 4); 
assertTrue(3 == copiedArray.length); assertTrue(copiedArray[0] == array[1]); assertTrue(copiedArray[1] == array[2]); assertTrue(copiedArray[2] == array[3]);

วิธีการทั้งสองนี้ทำสำเนาวัตถุแบบตื้นๆ หากใช้กับอาร์เรย์ของประเภทวัตถุที่ไม่ใช่ดั้งเดิม มาดูตัวอย่างกรณีทดสอบ:

Employee[] copiedArray = Arrays.copyOf(employees, employees.length); employees[0].setName(employees[0].getName() + "_Changed"); assertArrayEquals(copiedArray, array);

เนื่องจากผลลัพธ์เป็นสำเนาตื้นการเปลี่ยนชื่อพนักงานขององค์ประกอบของอาร์เรย์ดั้งเดิมทำให้เกิดการเปลี่ยนแปลงในสำเนาอาร์เรย์

ดังนั้น - หากเราต้องการทำสำเนาลึกของประเภทที่ไม่ใช่ดั้งเดิม - เราสามารถไปที่ตัวเลือกอื่น ๆ ที่อธิบายไว้ในส่วนที่จะมาถึง

4. คัดลอกอาร์เรย์ด้วยObject.clone ()

Object.clone ()สืบทอดมาจากคลาสObjectในอาร์เรย์

ก่อนอื่นเรามาคัดลอกอาร์เรย์ของประเภทดั้งเดิมโดยใช้วิธีการโคลน:

int[] array = {23, 43, 55, 12}; int[] copiedArray = array.clone(); 

และหลักฐานว่าใช้งานได้:

assertArrayEquals(copiedArray, array); array[0] = 9; assertTrue(copiedArray[0] != array[0]);

ตัวอย่างข้างต้นแสดงให้เห็นว่ามีเนื้อหาเดียวกันหลังจากการโคลน แต่มีการอ้างอิงที่แตกต่างกันดังนั้นการเปลี่ยนแปลงใด ๆ ในส่วนใดก็ตามจะไม่ส่งผลกระทบต่อเนื้อหา

ในทางกลับกันถ้าเราโคลนอาร์เรย์ของประเภทที่ไม่ใช่ดั้งเดิมโดยใช้วิธีการเดียวกันผลลัพธ์ก็จะแตกต่างกัน

สร้างสำเนาตื้นขององค์ประกอบอาร์เรย์ประเภทที่ไม่ใช่ดั้งเดิมแม้ว่าคลาสของอ็อบเจ็กต์ที่ปิดล้อมจะใช้อินเทอร์เฟซCloneableและลบล้างเมธอดclone ()จากคลาสObject

ลองดูตัวอย่าง:

public class Address implements Cloneable { // ... @Override protected Object clone() throws CloneNotSupportedException { super.clone(); Address address = new Address(); address.setCity(this.city); return address; } } 

เราสามารถทดสอบการใช้งานของเราได้โดยสร้างอาร์เรย์ที่อยู่ใหม่และเรียกใช้วิธีการclone ()ของเรา:

Address[] addresses = createAddressArray(); Address[] copiedArray = addresses.clone(); addresses[0].setCity(addresses[0].getCity() + "_Changed"); 
assertArrayEquals(copiedArray, addresses);

ตัวอย่างนี้แสดงให้เห็นว่าการเปลี่ยนแปลงใด ๆ ในอาร์เรย์ดั้งเดิมหรือที่คัดลอกจะทำให้เกิดการเปลี่ยนแปลงในอาร์เรย์อื่นแม้ว่าอ็อบเจ็กต์ที่ปิดล้อมจะสามารถโคลนได้

5. การใช้Stream API

ปรากฎว่าเราสามารถใช้ Stream API เพื่อคัดลอกอาร์เรย์ได้เช่นกัน ลองดูตัวอย่าง:

String[] strArray = {"orange", "red", "green'"}; String[] copiedArray = Arrays.stream(strArray).toArray(String[]::new); 

สำหรับประเภทที่ไม่ใช่ดั้งเดิมมันจะทำสำเนาวัตถุแบบตื้น ๆ ด้วย หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับJava 8 Streamsคุณสามารถเริ่มได้ที่นี่

6. ห้องสมุดภายนอก

Apache Commons 3 offers a utility class called SerializationUtils that provides a clone(…) method. It is very useful if we need to do a deep copy of an array of non-primitive types. It can be downloaded from here and its Maven dependency is:

 org.apache.commons commons-lang3 3.5  

Let's have a look at a test case:

public class Employee implements Serializable { // fields // standard getters and setters } Employee[] employees = createEmployeesArray(); Employee[] copiedArray = SerializationUtils.clone(employees); 
employees[0].setName(employees[0].getName() + "_Changed"); assertFalse( copiedArray[0].getName().equals(employees[0].getName()));

This class requires that each object should implement the Serializable interface. In terms of performance, it is slower than the clone methods written manually for each of the objects in our object graph to copy.

7. Conclusion

In this tutorial, we had a look at the various options to copy an array in Java.

วิธีการใช้ส่วนใหญ่ขึ้นอยู่กับสถานการณ์ที่แน่นอน ตราบใดที่เราใช้อาร์เรย์ประเภทดั้งเดิมเราสามารถใช้วิธีการใดก็ได้ที่นำเสนอโดยคลาสSystem and Arrays ประสิทธิภาพไม่น่าจะแตกต่างกัน

สำหรับประเภทที่ไม่ใช่แบบดั้งเดิมหากเราต้องการทำสำเนาอาร์เรย์แบบลึกเราสามารถใช้SerializationUtilsหรือเพิ่มวิธีการโคลนในคลาสของเรา

และเช่นเคยตัวอย่างที่แสดงในบทความนี้มีอยู่ใน GitHub