2013-07-18 28 views
6

tôi có phương pháp này varargs đơn giản mà chia từng hạng mục trong danh sách:Phương pháp Varargs thay đổi mảng của người gọi thay vì bản sao của riêng nó?

import java.util.*; 
class A { 
    static long f(long... xs) { 
     Arrays.sort(xs); 
     long y = 100000000; 
     for (int i = xs.length - 1; i >= 0; i--) 
     y /= xs[i]; 
     return y; 
    } 
    static { 
     System.out.println(f(5,2,6,3,9,3,13,4,5)); 
     long[] xs = new long[]{5,2,6,3,9,3,13,4,5}; 
     System.out.println(Arrays.toString(xs)); 
     System.out.println(f(xs)); 
     System.out.println(Arrays.toString(xs)); 
    } 
} 

tôi mong đợi nó để vượt qua một bản sao của mảng, nhưng dường như nó bằng cách nào đó sửa đổi các mảng tôi vượt qua trong, thay vì nó bản sao cục bộ riêng:

$ javac A.java && java A 
79 
[5, 2, 6, 3, 9, 3, 13, 4, 5] 
79 
[2, 3, 3, 4, 5, 5, 6, 9, 13] 

Vì vậy, tôi đã viết chương trình này kiểm tra đơn giản:

class B { 
    static void f(Object... os) { 
     System.out.println(os); 
    } 
    static { 
     Object os = new Object[]{1,2,3}; 
     System.out.println(os); 
     f(os); 
    } 
} 

Và nó làm những gì tôi mong đợi, nó bắt chước các BEF mảng đối tượng quặng đi qua nó vào f (do đó định danh đối tượng khác nhau):

$ javac B.java && java B 
[Ljava.lang.Object;@1242719c 
[Ljava.lang.Object;@4830c221 

Vậy làm thế nào sau đó là f trong A sửa đổi mảng của người gọi thay vì sao chép riêng của mình?

+4

Bản sao Java tham chiếu theo giá trị. Nó không bao giờ sao chép hoặc nhân bản các đối tượng cũng như mảng làm đối số (hoặc bất kỳ tình huống nào khác) trừ khi bạn làm điều đó một cách rõ ràng. –

Trả lời

14

Dường như bạn đã lừa mình ở đây:

Object os = new Object[]{1,2,3}; 
System.out.println(os); 
f(os); 

Kể từ os được gõ như Object, nó được hiểu là phần tử đầu tiên của mảng varargs. Những gì được chuyển vào phương thức thực sự là một Object[] mới có phần tử đơn là Object[] của bạn.

Nếu bạn làm như sau, nó sẽ in cùng một ví dụ:

Object[] os = new Object[]{1,2,3}; 
System.out.println(os); 
f(os); 

Phương pháp f sẽ cần tạo một bản sao phòng thủ của mảng chính nó để đảm bảo rằng một mảng thông qua vào bởi isn gọi không được sửa đổi. Như arshajii chỉ ra, varargs là trước hết các thông số mảng, với hành vi "tiền thưởng" của việc tạo một mảng mới khi đưa ra một danh sách đối số.

Dù sao bạn có thể sử dụng Arrays.copyOf để tạo bản sao, ủy quyền này (loại kém an toàn hơn) System.arraycopy.

+4

+1 Bắt tốt. Điều đáng nói đến là phương pháp varargs là * chính xác * giống như phương thức tương đương lấy tham số mảng ngoại trừ khi bạn chuyển nhiều đối số cho phương thức varargs, một mảng được tạo cho bạn. – arshajii

-1

Cuối cùng, mảng là một đối tượng, do đó bạn không sửa đổi tham chiếu mảng thay vì nội dung của nó được cho phép.

+1

Có, nhưng ông sửa đổi nội dung của một mảng khác. Và đây là những nguyên thủy, không có tài liệu tham khảo. – f1sh

+0

@ f1sh OP sửa đổi các giá trị mảng trong phương thức, vì vậy sau khi gọi nó trên mảng nội dung của nó sẽ được sửa đổi. –

3

varargs một loại mảng, nhưng với đường cú pháp để cho phép tạo mảng trực tiếp nếu các phần tử được chuyển riêng biệt dưới dạng tham số.

Đó là, hai chữ ký này là giống hệt nhau:

static long f(long... xs) { 
static long f(long[] xs) { 

Trừ rằng varargs có thể được gọi với các yếu tố riêng biệt thay vì một mảng

Tất nhiên mảng sẽ được sửa đổi nếu bạn bỏ qua trên tạo ra tự do và tạo một mảng cho chính mình để vượt qua.

1

Vậy làm thế nào sau đó f trong A sửa đổi mảng của người gọi thay vì bản sao của riêng nó?

Nó không có bản sao riêng. Nó có một tham chiếu đến mảng của người gọi.

Các vấn đề liên quan