Cách dễ nhất để chuyển đổi bộ sưu tập thành mảng?


125

Giả sử ta có a Collection<Foo>. Cách tốt nhất (ngắn nhất trong LoC trong bối cảnh hiện tại) là gì để chuyển đổi nó thànhFoo[] gì? Bất kỳ thư viện nổi tiếng đều được cho phép.

CẬP NHẬT: (thêm một trường hợp trong phần này; để lại nhận xét nếu bạn nghĩ rằng nó đáng để tạo một chủ đề khác cho nó): Điều gì về việc chuyển đổi Collection<Foo>sang Bar[]nơi Barcó hàm tạo với 1 tham số loại Footức là public Bar(Foo foo){ ... }?


Đưa ra câu trả lời này bằng một API thay thế được giới thiệu trong JDK-11 để thực hiện cùng một hoạt động với hiệu suất tương tự và lời giải thích cùng với nó. Cộng với cú pháp khớp với Stream.toArrayAPI hiện có từ JDK.
Naman

Câu trả lời:


256

Trường hợp xlà bộ sưu tập:

Foo[] foos = x.toArray(new Foo[x.size()]);

35
đơn giản hơn (dễ dàng hơn) nhưng không tốt nhất (bộ nhớ): x.toArray(new Foo[0]) --- tài liệu : không có thời gian để đọc ...
user85421

6
@Carlos thực sự, nó sử dụng bộ nhớ tốt hơn (new Foo[0]). Theo tài liệu Collection.toArray, `@param một mảng mà các phần tử của bộ sưu tập này sẽ được lưu trữ, nếu nó đủ lớn 'có nghĩa là nó sẽ lưu trữ chúng trực tiếp trong mảng mới đó. Nếu bạn cung cấp cho nó một mảng kích thước 0, nó sẽ tạo ra một mảng mới, có nghĩa là bạn có một mảng nhỏ và một mảng lớn được tạo ra khi điều đó là không cần thiết.
corsiKa

4
@glowcoder - nhưng điều này có thể được giảm thiểu nếu bạn xác định một mảng trống một lần là hằng số tĩnh. Đó chỉ là một gợi ý toArrayđể tạo một mảng mục tiêu đúng loại. Và trong trường hợp này, thành thật mà nói, tôi sẽ không quan tâm đến thêm một mảng trống trong ứng dụng của mình. Đó là dưới mức độ tiếng ồn.
Andreas Dolk

2
@glowcoder - đó chính xác là lý do tại sao tôi ( đã cố gắng ) viết rằng sử dụng new Foo[0]đơn giản hơn "nhưng không tốt nhất (bộ nhớ)" ... đó là ý tôi là giải pháp của tôi dễ hơn nhưng không tốt nhất (đó là lý do tôi sử dụng :).
dùng85421

21
Lưu ý rằng mã này không phải là chủ đề an toàn, ngay cả khi bạn đang sử dụng bộ sưu tập được đồng bộ hóa. Nó sẽ tự xử lý trong hầu hết các tình huống, nhưng nếu một mục bị xóa giữa lệnh gọi x.size () và x.toArray (), mảng kết quả sẽ chứa một phần tử null bổ sung ở cuối. Các new Foo[0]biến thể được đề xuất bởi Carlos không gặp phải vấn đề này.
Jules

36

Giải pháp thay thế cho câu hỏi được cập nhật bằng Java 8:

Bar[] result = foos.stream()
    .map(x -> new Bar(x))
    .toArray(size -> new Bar[size]);

35
Điều này có thể được rút ngắn thành Bar[] result = foos.stream().map(Bar::new).toArray(Bar[]::new);
lpandzic

Cú pháp khôn ngoan Tôi rất thích câu trả lời được chấp nhận. Tôi ước mọi người có thể chuyển sang Python.
Eric Chen

@EricChen Chuyển sang Python - một ngôn ngữ được dịch và gõ động từ Java - 'ngôn ngữ được biên dịch và gõ tĩnh' vì cú pháp mã hóa và các dòng số khác nhau? Không phải là một ý tưởng tốt.
RafiAlhamd

10

Nếu bạn sử dụng nó nhiều lần hoặc trong một vòng lặp, bạn có thể xác định một hằng số

public static final Foo[] FOO = new Foo[]{};

và thực hiện chuyển đổi nó như thế nào

Foo[] foos = fooCollection.toArray(FOO);

Các toArrayphương pháp sẽ mất mảng trống để xác định đúng loại của mảng mục tiêu và tạo ra một mảng mới cho bạn.


Đây là đề xuất của tôi cho bản cập nhật:

Collection<Foo> foos = new ArrayList<Foo>();
Collection<Bar> temp = new ArrayList<Bar>();
for (Foo foo:foos) 
    temp.add(new Bar(foo));
Bar[] bars = temp.toArray(new Bar[]{});

1
up, constkhông phải là Java! public static final Foo[] FOO = {}
dùng85421

1
Tại sao nó sẽ được công khai?
Zoltán

@ Zoltán - tại sao không? Đó là bất biến, vì vậy không có vấn đề an toàn. Đó là một chút lộn xộn, nhưng có thể hữu ích trong các mã khác, vì vậy nói chung là vô hại. Thậm chí có thể tạo ra một lớp trung tâm với tất cả các hằng số này, và sau đó sử dụng import staticđể lấy chúng.
Jules

@Jules tham chiếu đến mảng là bất biến, nhưng nội dung của nó thì không. Bất cứ ai ngoài lớp có thể thay thế các phần tử của mảng một cách tự do. Mặt khác, như một quy luật tự nhiên, tôi tin rằng tất cả các lĩnh vực nên được đặt ở chế độ riêng tư cho đến khi chúng cần thiết bên ngoài lớp học.
Zoltán

2
Có độ dài bằng không, không có nội dung nào có thể thay đổi
Jules

5

Với JDK / 11, một cách khác để chuyển đổi Collection<Foo>thành một Foo[]có thể là sử dụng Collection.toArray(IntFunction<T[]> generator)như:

Foo[] foos = fooCollection.toArray(new Foo[0]); // before JDK 11
Foo[] updatedFoos = fooCollection.toArray(Foo[]::new); // after JDK 11

Như được giải thích bởi @Stuart trong danh sách gửi thư (nhấn mạnh của tôi), hiệu suất của việc này về cơ bản phải giống như của hiện tại Collection.toArray(new T[0])-

Kết quả cuối cùng là việc triển khai sử dụng Arrays.copyOf() là nhanh nhất, có thể vì đó là nội tại .

Nó có thể tránh không điền vào mảng được phân bổ mới vì nó biết toàn bộ nội dung mảng sẽ bị ghi đè. Điều này đúng bất kể API công khai trông như thế nào.

Việc triển khai API trong JDK đọc:

default <T> T[] toArray(IntFunction<T[]> generator) {
    return toArray(generator.apply(0));
}

Việc thực hiện mặc định gọi generator.apply(0)để có được một mảng có độ dài bằng không và sau đó chỉ cần gọi toArray(T[]). Điều này đi qua Arrays.copyOf() con đường nhanh, vì vậy về cơ bản nó có cùng tốc độ toArray(new T[0]).


Lưu ý : - Chỉ cần sử dụng API sẽ được hướng dẫn cùng với tính không tương thích ngược khi được sử dụng cho mã vớinullcác giá trị, ví dụ nhưtoArray(null)các cuộc gọi này bây giờ sẽ không rõ ràng vì hiện cótoArray(T[] a)và sẽ không biên dịch được.



3

Đối với bản gốc xem câu trả lời doublep :

Foo[] a = x.toArray(new Foo[x.size()]);

Về phần cập nhật:

int i = 0;
Bar[] bars = new Bar[fooCollection.size()];
for( Foo foo : fooCollection ) { // where fooCollection is Collection<Foo>
    bars[i++] = new Bar(foo);
}    

3

Đây là giải pháp cuối cùng cho trường hợp trong phần cập nhật (với sự trợ giúp của Bộ sưu tập Google):

Collections2.transform (fooCollection, new Function<Foo, Bar>() {
    public Bar apply (Foo foo) {
        return new Bar (foo);
    }
}).toArray (new Bar[fooCollection.size()]);

Nhưng, cách tiếp cận chính ở đây đã được đề cập trong câu trả lời của doublep (tôi quên toArrayphương pháp).


1
Xem nhận xét của tôi về câu trả lời của doublep về an toàn luồng, cũng áp dụng cho giải pháp này. new Bar[0]tránh vấn đề
Jules

-1

Ví dụ: bạn có bộ sưu tập ArrayList với các thành phần Lớp sinh viên:

List stuList = new ArrayList();
Student s1 = new Student("Raju");
Student s2 = new Student("Harish");
stuList.add(s1);
stuList.add(s2);
//now you can convert this collection stuList to Array like this
Object[] stuArr = stuList.toArray();           // <----- toArray() function will convert collection to array
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.