Tạo bản sao của một mảng


345

Tôi có một mảng aliên tục được cập nhật. Hãy nói a = [1,2,3,4,5]. Tôi cần phải tạo một bản sao chính xác avà gọi nó b. Nếu ađược đổi thành [6,7,8,9,10], bvẫn nên [1,2,3,4,5]. Cách tốt nhất để làm việc này là gì? Tôi đã thử một forvòng lặp như:

for(int i=0; i<5; i++) {
    b[i]=a[i]
}

nhưng điều đó dường như không hoạt động chính xác. Vui lòng không sử dụng các thuật ngữ nâng cao như bản sao sâu, v.v., vì tôi không biết điều đó có nghĩa là gì.

Câu trả lời:


558

Bạn có thể thử sử dụng System.arraycopy ()

int[] src  = new int[]{1,2,3,4,5};
int[] dest = new int[5];

System.arraycopy( src, 0, dest, 0, src.length );

Nhưng, có lẽ tốt hơn để sử dụng clone () trong hầu hết các trường hợp:

int[] src = ...
int[] dest = src.clone();

9
+1 vì không làm lại bánh xe. Và theo như tôi biết, giải pháp này là nhanh hơn bạn có thể nhận được trong sao chép mảng.
Felipe Hummel

6
cả bản sao và bản sao là bản địa. Tôi hy vọng bản sao sẽ nhanh hơn một chút. không phải là sự khác biệt
MeBigFatGuy

5
@Felipe, @MeBigFatGuy - chỉ dành cho một mảng lớn. Đối với một mảng nhỏ, một vòng lặp sao chép có thể nhanh hơn do các chi phí thiết lập. Nếu bạn nhìn vào javadoc System.arraycopy, bạn sẽ thấy rằng phương thức này cần kiểm tra nhiều thứ khác nhau trước khi bắt đầu. Một số kiểm tra này là không cần thiết với một vòng lặp sao chép, tùy thuộc vào các loại mảng tĩnh.
Stephen C

7
@FelipeHummel, @MeBigFatGuy, @StephenC - Đây là một bài kiểm tra hiệu suất của các phương pháp sao chép mảng được đề cập trong các câu trả lời ở đây. Trong đó thiết lập, clone()hóa ra là nhanh nhất cho 250 000 phần tử.
Adam

6
Thật đáng thất vọng khi thấy rằng tất cả các cuộc thảo luận ở đây là về các vấn đề hiệu suất vi mô, mà 99,999% thời gian, không quan trọng. Điểm quan trọng hơn src.clone()là dễ đọc hơn và có ít cơ hội hơn cho lỗi hơn là phân bổ một mảng mới và thực hiện arraycopy. (Và cũng xảy ra nhanh.)
Brian Goetz

231

bạn có thể dùng

int[] a = new int[]{1,2,3,4,5};
int[] b = a.clone();

cũng.


6
Tôi chỉ xóa điểm của OP rằng: " Nếu A thay đổi thành [6,7,8,9,10], thì B vẫn là [1,2,3,4,5] ". OP cho biết anh đã thử sử dụng loop nhưng không hiệu quả với anh.
Harry Joy

15
Các diễn viên là không cần thiết; một máy phân tích tĩnh tốt sẽ cảnh báo về nó. Nhưng nhân bản chắc chắn là cách tốt nhất để tạo một bản sao mới của một mảng.
erickson

5
@MeBigFatGuy - trường hợp sử dụng của OP đòi hỏi phải sao chép lặp lại vào cùng một mảng, vì vậy bản sao không hoạt động.
Stephen C

4
@Stephen C, tôi đã không đọc nó. Tôi vừa đọc anh ấy muốn một bản sao, và sau đó sẽ liên tục cập nhật phiên bản không được lưu trữ.
MeBigFatGuy

4
@MeBigFatGuy - anh ấy nói "Tôi có một mảng A liên tục được cập nhật." . Có lẽ tôi đang đọc quá nhiều về điều đó, nhưng tôi coi điều này là ngụ ý rằng anh ta cũng liên tục sao chép A sang B.
Stephen C

184

Nếu bạn muốn tạo một bản sao của:

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

Đây là con đường để đi:

int[] b = Arrays.copyOf(a, a.length);

Arrays.copyOfcó thể nhanh hơn a.clone()trên các mảng nhỏ. Cả hai phần tử sao chép đều nhanh như nhau nhưng clone () trả về Objectnên trình biên dịch phải chèn một hàm ẩn vào int[]. Bạn có thể thấy nó trong mã byte, một cái gì đó như thế này:

ALOAD 1
INVOKEVIRTUAL [I.clone ()Ljava/lang/Object;
CHECKCAST [I
ASTORE 2

62

Lời giải thích hay từ http://www.journaldev.com/753/how-to-copy-arrays-in-java

Các phương thức sao chép mảng Java

Object.clone () : Lớp Object cung cấp phương thức clone () và vì mảng trong java cũng là một Object, bạn có thể sử dụng phương thức này để đạt được bản sao mảng đầy đủ. Phương pháp này sẽ không phù hợp với bạn nếu bạn muốn sao chép một phần mảng.

System.arraycopy () : Mảng lớp hệ thống () là cách tốt nhất để thực hiện sao chép một phần của một mảng. Nó cung cấp cho bạn một cách dễ dàng để chỉ định tổng số phần tử cần sao chép và các vị trí chỉ mục mảng nguồn và đích. Ví dụ: System.arraycopy (nguồn, 3, đích, 2, 5) sẽ sao chép 5 phần tử từ nguồn đến đích, bắt đầu từ chỉ mục thứ 3 của nguồn sang chỉ mục đích thứ 2.

Arrays.copyOf (): Nếu bạn muốn sao chép một vài phần tử đầu tiên của một mảng hoặc bản sao đầy đủ của mảng, bạn có thể sử dụng phương thức này. Rõ ràng nó không linh hoạt như System.arraycopy () nhưng nó cũng không khó hiểu và dễ sử dụng.

Arrays.copyOfRange () : Nếu bạn muốn một vài phần tử của một mảng được sao chép, trong đó chỉ số bắt đầu không phải là 0, bạn có thể sử dụng phương thức này để sao chép một phần mảng.


35

Tôi có cảm giác rằng tất cả những "cách tốt hơn để sao chép một mảng" sẽ không thực sự giải quyết vấn đề của bạn.

Bạn nói

Tôi đã thử một vòng lặp for như [...] nhưng dường như nó không hoạt động chính xác?

Nhìn vào vòng lặp đó, không có lý do rõ ràng nào để nó không hoạt động ... trừ khi:

  • bạn bằng cách nào đó có abcác mảng bị rối (ví dụ abtham chiếu đến cùng một mảng), hoặc
  • ứng dụng của bạn là đa luồng và các luồng khác nhau đang đọc và cập nhật amảng đồng thời.

Trong cả hai trường hợp, các cách khác để thực hiện sao chép sẽ không giải quyết được vấn đề tiềm ẩn.

Việc sửa chữa cho kịch bản đầu tiên là rõ ràng. Đối với kịch bản thứ hai, bạn sẽ phải tìm ra một số cách đồng bộ hóa các luồng. Các lớp mảng nguyên tử không giúp được gì vì chúng không có các hàm tạo sao chép nguyên tử hoặc các phương thức sao chép, nhưng đồng bộ hóa bằng cách sử dụng một mutex nguyên thủy sẽ thực hiện thủ thuật.

(Có những gợi ý trong câu hỏi của bạn khiến tôi nghĩ rằng đây thực sự có liên quan đến chủ đề; ví dụ như tuyên bố của bạn aliên tục thay đổi.)


2
đồng ý .. có lẽ đúng.
MeBigFatGuy


9

Tất cả các giải pháp gọi độ dài từ mảng, thêm ví dụ mã kiểm tra null dự phòng mã của bạn:

int[] a = {1,2,3,4,5};
int[] b = Arrays.copyOf(a, a.length);
int[] c = a.clone();

//What if array a comes as local parameter? You need to use null check:

public void someMethod(int[] a) {
    if (a!=null) {
        int[] b = Arrays.copyOf(a, a.length);
        int[] c = a.clone();
    }
}

Tôi khuyên bạn không nên phát minh ra bánh xe và sử dụng lớp tiện ích nơi tất cả các kiểm tra cần thiết đã được thực hiện. Hãy xem xét ArrayUtils từ apache commons. Mã của bạn trở nên ngắn hơn:

public void someMethod(int[] a) {
    int[] b = ArrayUtils.clone(a);
}

Commons Apache bạn có thể tìm thấy ở đó


8

Bạn cũng có thể sử dụng Arrays.copyOfRange.

Ví dụ :

public static void main(String[] args) {
    int[] a = {1,2,3};
    int[] b = Arrays.copyOfRange(a, 0, a.length);
    a[0] = 5;
    System.out.println(Arrays.toString(a)); // [5,2,3]
    System.out.println(Arrays.toString(b)); // [1,2,3]
}

Phương pháp này tương tự Arrays.copyOf, nhưng nó linh hoạt hơn. Cả hai đều sử dụng System.arraycopydưới mui xe.

Xem :


3

Đối với một bản sao an toàn null của một mảng, bạn cũng có thể sử dụng một tùy chọn với Object.clone()phương thức được cung cấp trong câu trả lời này .

int[] arrayToCopy = {1, 2, 3};
int[] copiedArray = Optional.ofNullable(arrayToCopy).map(int[]::clone).orElse(null);

Mặc dù giải pháp này quá phức tạp, nó cũng gây lãng phí bộ nhớ và nếu mảng chứa một bí mật (ví dụ: mảng byte có mật khẩu), nó cũng đưa ra các lỗi bảo mật vì các đối tượng trung gian sẽ nằm trên đống cho đến khi bộ sưu tập rác có thể bị lộ cho những kẻ tấn công.
Weltraumschaf

1
Tôi không đồng ý rằng mảng sẽ nằm trên đống đặc biệt cho cấu trúc này. Thật vậy, nó chỉ gọi bản sao khi cần thiết và Optionalđối tượng chỉ là một đối tượng trống với tham chiếu đến mảng hiện có. Về tác động hiệu năng, tôi sẽ nói rằng còn sớm để nói rằng nó thực sự là một tác động vì kiểu xây dựng này là một ứng cử viên tốt để đưa vào bên trong JVM và sau đó không ảnh hưởng nhiều hơn các phương thức khác. Đó là vấn đề về phong cách (lập trình chức năng so với lập trình thủ tục nhưng không chỉ) để xem xét nó có phức tạp hơn hay không.
Nicolas Henneaux

3

Nếu bạn phải làm việc với mảng liệu và không ArrayListsau đó Arrayscó những gì bạn cần. Nếu bạn nhìn vào mã nguồn, đây là những cách hoàn toàn tốt nhất để có được một bản sao của một mảng. Họ có một chút lập trình phòng thủ tốt bởi vì System.arraycopy()phương thức này ném ra rất nhiều ngoại lệ không được kiểm soát nếu bạn cung cấp cho nó các tham số phi logic.

Bạn có thể sử dụng một trong hai Arrays.copyOf()sẽ sao chép từ Nthphần tử đầu tiên sang phần tử ngắn hơn.

public static <T> T[] copyOf(T[] original, int newLength)

Sao chép mảng đã chỉ định, cắt bớt hoặc đệm bằng null (nếu cần) để bản sao có độ dài được chỉ định. Đối với tất cả các chỉ mục hợp lệ trong cả mảng gốc và bản sao, hai mảng sẽ chứa các giá trị giống hệt nhau. Đối với bất kỳ chỉ số nào hợp lệ trong bản sao nhưng không phải bản gốc, bản sao sẽ chứa null. Các chỉ số như vậy sẽ tồn tại khi và chỉ khi độ dài được chỉ định lớn hơn độ dài của mảng ban đầu. Mảng kết quả là chính xác cùng lớp với mảng ban đầu.

2770
2771    public static <T,U> T[] More ...copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
2772        T[] copy = ((Object)newType == (Object)Object[].class)
2773            ? (T[]) new Object[newLength]
2774            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
2775        System.arraycopy(original, 0, copy, 0,
2776                         Math.min(original.length, newLength));
2777        return copy;
2778    }

hoặc Arrays.copyOfRange()cũng sẽ thực hiện các mẹo:

public static <T> T[] copyOfRange(T[] original, int from, int to)

Sao chép phạm vi chỉ định của mảng đã chỉ định thành một mảng mới. Chỉ số ban đầu của phạm vi (từ) phải nằm giữa 0 và gốc.length, đã bao gồm. Giá trị tại bản gốc [từ] được đặt vào phần tử ban đầu của bản sao (trừ khi từ == original.length hoặc từ == đến). Các giá trị từ các phần tử tiếp theo trong mảng ban đầu được đặt vào các phần tử tiếp theo trong bản sao. Chỉ mục cuối cùng của phạm vi (đến), phải lớn hơn hoặc bằng từ, có thể lớn hơn gốc.length, trong trường hợp null được đặt trong tất cả các thành phần của bản sao có chỉ số lớn hơn hoặc bằng gốc. chiều dài - từ. Độ dài của mảng trả về sẽ là - từ. Mảng kết quả là chính xác cùng lớp với mảng ban đầu.

3035    public static <T,U> T[] More ...copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
3036        int newLength = to - from;
3037        if (newLength < 0)
3038            throw new IllegalArgumentException(from + " > " + to);
3039        T[] copy = ((Object)newType == (Object)Object[].class)
3040            ? (T[]) new Object[newLength]
3041            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
3042        System.arraycopy(original, from, copy, 0,
3043                         Math.min(original.length - from, newLength));
3044        return copy;
3045    }

Như bạn có thể thấy, cả hai đều chỉ là các hàm bao bọc System.arraycopyvới logic phòng thủ mà những gì bạn đang cố gắng làm là hợp lệ.

System.arraycopy là cách nhanh nhất tuyệt đối để sao chép mảng.


0

Tôi đã có một vấn đề tương tự với mảng 2D và kết thúc ở đây. Tôi đã sao chép mảng chính và thay đổi giá trị của mảng bên trong và rất ngạc nhiên khi các giá trị thay đổi trong cả hai bản sao. Về cơ bản cả hai bản sao là phụ thuộc nhưng có chứa các tham chiếu đến cùng một mảng bên trong và tôi phải tạo ra một loạt các bản sao của các mảng bên trong để có được những gì tôi muốn.

Đây có thể không phải là vấn đề của OP, nhưng tôi hy vọng nó vẫn có thể hữu ích.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.