Tại sao một mảng không thể gán cho Iterable?


186

với Java5 chúng ta có thể viết:

Foo[] foos = ...
for (Foo foo : foos) 

hoặc chỉ sử dụng một Iterable trong vòng lặp for. Điều này rất tiện dụng.

Tuy nhiên, bạn không thể viết một phương thức chung cho iterable như thế này:

public void bar(Iterable<Foo> foos) { .. }

và gọi nó bằng một mảng vì nó không phải là Iterable:

Foo[] foos = { .. };
bar(foos);  // compile time error 

Tôi đang tự hỏi về những lý do đằng sau quyết định thiết kế này.


8
Arrays.asList là đủ tốt, tôi cho rằng
dfa

17
đó là một câu hỏi triết học
dfa

2
một lý do chính đáng để xử lý các mảng trong Java 5+ là các phương thức varargs.
Jeff Walker

2
@Torsten: đúng, nhưng nếu bạn chuyển nó sang một phương thức chấp nhận Iterable, thì có khả năng bạn sẽ không thực hiện bất kỳ thay đổi nào.
Michael Myers

5
Trên thực tế, Arrays.asList không đủ tốt vì nó không hoạt động trên các mảng của các kiểu nguyên thủy. Cách tích hợp duy nhất để lặp lại các phần tử (đóng hộp) chung của các kiểu nguyên thủy là với sự phản chiếu, sử dụng java.lang.reflect.Array, nhưng hiệu suất của nó là yếu. Tuy nhiên, bạn có thể viết các trình lặp của riêng mình (hoặc liệt kê danh sách!) Để bọc các mảng kiểu nguyên thủy nếu bạn muốn.
Boann

Câu trả lời:


78

Mảng có thể thực hiện các giao diện ( Cloneablejava.io.Serializable). Vậy tại sao không Iterable? Tôi đoán Iterablecác lực lượng thêm một iteratorphương thức và các mảng không thực hiện các phương thức. char[]thậm chí không ghi đè toString. Dù sao, mảng tham chiếu nên được coi là ít hơn lý tưởng - sử dụng Lists. Như ý kiến ​​dfa, Arrays.asListsẽ thực hiện chuyển đổi cho bạn, rõ ràng.

(Đã nói rằng, bạn có thể gọi clonetrên mảng.)


23
> "... và mảng không thực hiện các phương thức." Tôi nghĩ rằng đây là một câu hỏi triết học khác; mảng không bao giờ là kiểu nguyên thủy và triết lý Java đọc rằng "Mọi thứ đều là đối tượng (ngoại trừ kiểu nguyên thủy)". Tại sao, sau đó, các mảng không thực hiện các phương thức mặc dù có một hoạt động đáng kinh ngạc mà người ta muốn sử dụng một mảng cho từ đầu. Ồ, đúng vậy, mảng là bộ sưu tập được đánh máy mạnh mẽ duy nhất trước khi thuốc generic xuất hiện như một trở ngại đáng tiếc.
fatuhoku

2
Nếu bạn đã có dữ liệu trong một mảng, có thể bạn đang thực hiện công việc quan trọng ở mức độ thấp, hiệu suất như xử lý việc đọc byte [] của luồng. Việc không thể lặp lại các mảng có thể xuất phát từ các tổng quát Java không hỗ trợ các nguyên hàm như các đối số kiểu, như @Gareth nói dưới đây.
Drew Noakes

2
@FatuHoku Gợi ý khái quát là một trở ngại đáng tiếc là không chính xác. Sự mong muốn của thuốc generic luôn được đánh giá cao. Mảng không phải là nguyên thủy (tôi không nói là như vậy), nhưng chúng ở cấp độ thấp. Một điều bạn muốn làm với mảng là sử dụng chúng làm chi tiết triển khai cho các cấu trúc giống như vectơ.
Tom Hawtin - tackline

1
Iterator<T>cũng yêu cầu remove(T), mặc dù nó được phép ném UnsupportedOperationException.
wchargein

Mảng thực hiện các phương thức: chúng thực hiện tất cả các phương thức java.lang.Object.
mhsmith

59

Mảng là một đối tượng, nhưng các mục của nó có thể không. Mảng có thể chứa một kiểu nguyên thủy như int, mà Iterable không thể đối phó được. Ít nhất đó là những gì tôi nghĩ.


3
Điều này có nghĩa là để hỗ trợ Iterablegiao diện, các mảng nguyên thủy phải được chuyên môn hóa để sử dụng các lớp bao bọc. Không ai trong số này thực sự là một vấn đề lớn, vì dù sao các tham số loại đều là giả mạo.
thejoshwolfe

8
Điều này sẽ không ngăn mảng Object thực hiện Iterable. Nó cũng sẽ không ngăn các mảng nguyên thủy thực hiện Iterable cho kiểu được bọc.
Boann

1
Autoboxing có thể xử lý việc này
Tim Büthe

Tôi nghĩ rằng đây là lý do chính xác. Nó sẽ không hoạt động thỏa đáng cho các mảng của các kiểu nguyên thủy, cho đến khi Generics hỗ trợ các kiểu nguyên thủy (ví dụ List<int>thay vì List<Integer>, v.v.). Một vụ hack có thể được thực hiện với các trình bao bọc nhưng bị giảm hiệu năng - và quan trọng hơn - nếu việc hack này được thực hiện, nó sẽ ngăn chặn việc triển khai nó đúng cách trong Java trong tương lai (ví dụ như int[].iterator()sẽ bị khóa vĩnh viễn Iterator<Integer>thay vì quay lại Iterator<int>). Có lẽ, các loại giá trị sắp tới + chuyên môn hóa chung cho Java (dự án valhalla) sẽ làm cho các mảng thực hiện Iterable.
Bjarke

16

Mảng phải hỗ trợ Iterable, họ chỉ không, vì lý do tương tự mà mảng .NET không hỗ trợ giao diện cho phép truy cập ngẫu nhiên chỉ đọc theo vị trí (không có giao diện nào được xác định là tiêu chuẩn). Về cơ bản, các khung công tác thường có những khoảng trống nhỏ khó chịu trong đó, điều này không đáng để ai có thời gian sửa chữa. Sẽ không có vấn đề gì nếu chúng ta có thể tự sửa chúng theo cách tối ưu, nhưng thường thì chúng ta không thể.

CẬP NHẬT: Để thuận tay, tôi đã đề cập đến các mảng .NET không hỗ trợ giao diện hỗ trợ truy cập ngẫu nhiên theo vị trí (xem thêm nhận xét của tôi). Nhưng trong .NET 4.5, giao diện chính xác đã được xác định và được hỗ trợ bởi các mảng và List<T>lớp:

IReadOnlyList<int> a = new[] {1, 2, 3, 4};
IReadOnlyList<int> b = new List<int> { 1, 2, 3, 4 };

Tất cả vẫn chưa hoàn hảo vì giao diện danh sách có thể thay đổi IList<T>không kế thừa IReadOnlyList<T>:

IList<int> c = new List<int> { 1, 2, 3, 4 };
IReadOnlyList<int> d = c; // error

Có lẽ có một khả năng tương thích ngược có thể có với một sự thay đổi như vậy.

Nếu có bất kỳ tiến triển nào về những điều tương tự trong các phiên bản Java mới hơn, tôi rất muốn biết trong các nhận xét! :)


8
Mảng .NET triển khai giao diện IList
Tom Gillen

2
@Aphid - Tôi nói truy cập ngẫu nhiên chỉ đọc . IList<T>phơi bày các hoạt động để sửa đổi. Nó sẽ là tuyệt vời nếu IList<T>có một cái gì đó được thừa kế như một IReadonlyList<T>giao diện, trong đó sẽ vừa có CountT this[int]và di truyền IEnumerable<T>(mà đã hỗ trợ liệt kê chỉ đọc). Một điều tuyệt vời khác sẽ là một giao diện để có được một bộ liệt kê thứ tự ngược, mà Reversephương thức mở rộng có thể truy vấn (giống như các Countphương thức mở rộng truy vấn ICollectionđể tự tối ưu hóa.)
Daniel Earwicker

Vâng, sẽ tốt hơn nhiều nếu mọi thứ được thiết kế theo cách đó. Giao diện IList xác định các thuộc tính IsReadOnly và IsFixedSize, được triển khai phù hợp theo mảng. Điều đó luôn luôn gây ấn tượng với tôi như là một cách làm rất tệ, vì nó không cung cấp thời gian biên dịch để kiểm tra xem danh sách bạn đưa ra có thực sự chỉ đọc và tôi rất hiếm khi thấy mã kiểm tra các thuộc tính này.
Tom Gillen

1
Mảng trong .NET hiện thực IList& ICollectionkể từ .NET 1.1 IList<T>ICollection<T>kể từ .NET 2.0. Đây là một trường hợp khác mà Java thua xa đối thủ.
Amir Abiri

@TomGillen: Vấn đề lớn nhất của tôi IListlà nó không cung cấp nhiều thuộc tính truy vấn hơn. Tôi muốn nói rằng một bộ thích hợp nên bao gồm IsUpdatizable, IsResizable, IsReadOnly, IsFixedSize và Ex HiệnElementsAreImmutable cho người mới bắt đầu. Câu hỏi về việc mã tham chiếu có thể, mà không cần đánh máy, sửa đổi danh sách có tách biệt với các câu hỏi về việc liệu mã có tham chiếu đến danh sách mà không cần sửa đổi có thể chia sẻ trực tiếp tham chiếu đó với mã bên ngoài hay không nó có thể giả định một cách an toàn một số khía cạnh của danh sách sẽ không bao giờ thay đổi.
supercat

14

Thật không may, mảng không 'không đủ class'. Họ không thực hiện Iterablegiao diện.

Mặc dù các mảng hiện là các đối tượng triển khai Clonable và serializable, tôi tin rằng một mảng không phải là một đối tượng theo nghĩa thông thường và không thực hiện giao diện.

Lý do bạn có thể sử dụng chúng trong mỗi vòng lặp là vì Sun đã thêm vào một số đường tổng hợp cho mảng (đó là trường hợp đặc biệt).

Do các mảng bắt đầu là 'gần như các đối tượng' với Java 1, nên sẽ có quá nhiều thay đổi để biến chúng thành các đối tượng thực sự trong Java.


14
Tuy nhiên, vẫn có đường cho mỗi vòng lặp, vậy tại sao không thể có đường cho Iterable?
Michael Myers

8
@mmyer: Đường được sử dụng cho mỗi loại là đường thời gian biên dịch . Điều đó dễ làm hơn nhiều so với VM đường. Phải nói rằng, mảng .NET tốt hơn đáng kể trong lĩnh vực này ...
Jon Skeet

12
Mảng có thể thực hiện giao diện. Họ thực hiện CloneableSerializablegiao diện.
notnoop

34
mảng java là đối tượng trong tất cả các giác quan. Vui lòng xóa thông tin sai lệch đó. Họ không thực hiện Iterable.
ykaganovich

5
Một mảng là một đối tượng. Nó hỗ trợ hữu ích : Các phương thức P như Wait (), Wait (n), Wait (n, m), notify (), notify ALL (), Finize (), triển khai vô nghĩa của toString () Phương thức hữu ích duy nhất là getClass () .
Peter Lawrey

1

Trình biên dịch thực sự dịch for eachtrên một mảng thành một forvòng lặp đơn giản với một biến đếm.

Tổng hợp như sau

public void doArrayForEach() {
    int[] ints = new int[5];

    for(int i : ints) {
        System.out.println(i);
    }
}

và sau đó dịch ngược tệp. class mang lại

public void doArrayForEach() {
    int[] ints = new int[5];
    int[] var2 = ints;
    int var3 = ints.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        int i = var2[var4];
        System.out.println(i);
    }
}
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.