Java: Nhận mục đầu tiên từ bộ sưu tập


277

Nếu tôi có một bộ sưu tập, chẳng hạn như Collection<String> strs, làm thế nào tôi có thể lấy ra món đồ đầu tiên? Tôi chỉ có thể gọi một Iterator, lấy nó trước next(), sau đó ném Iteratorđi. Có một cách ít lãng phí để làm điều đó?


1
Tất nhiên, có thể có một cách tốt hơn để truy cập vào phần tử đầu tiên nếu bạn biết lớp container triển khai ...
Rooke


1
Có vẻ như bạn cần Queue.peek ()
Johannes

Câu trả lời:


131

Iterables.get (yourC, indexYouWant)

Bởi vì thực sự, nếu bạn đang sử dụng Bộ sưu tập, bạn nên sử dụng Google Bộ sưu tập.


7
Điều đó cũng làm điều tương tự, nó chỉ kiểm tra xem nó có phải là một danh sách đầu tiên hay không và theo chỉ số nếu có. Nó cũng có một số mã để cố gắng thất bại nhanh hơn trên Bộ sưu tập thực tế (đó là nếu chỉ số quá lớn, nó sẽ cố gắng tìm ra điều đó mà không lặp lại toàn bộ và ném ngoại lệ vào cuối).
Yishai

1
Thành thật mà nói, hiệu năng-khôn ngoan có thể chậm hơn một chút so với c.iterator (). Next () - nhưng mã rõ ràng hơn và đơn giản hơn để sửa đổi.
Carl

2
Tôi chắc chắn đồng ý rằng nó sạch hơn, nhưng OP thì lãng phí, nhưng tôi đoán vì câu trả lời của bạn đã được chấp nhận, đó là điều mong muốn.
Yishai

8
Đối với những người (vẫn) đến đây: Tôi nghĩ rằng câu trả lời của jheddings có lẽ là câu trả lời "hoàn thành tốt nhất", mặc dù tôi thích @ DonaldRaab (đường xuống trang) cho các trường hợp tôi đã sử dụng thư viện GC. Câu trả lời của tôi thực sự dành cho trường hợp người ta có thể muốn viết linh hoạt cho lần sau (giả sử, nếu người ta quyết định rằng yếu tố thứ hai là độ nóng mới).
Carl

4
Đôi khi bạn chỉ sử dụng mã sử dụng Bộ sưu tập, vì vậy không có nhiều việc phải làm.
erickrf

436

Có vẻ như đó là cách tốt nhất để làm điều đó:

String first = strs.iterator().next();

Câu hỏi tuyệt vời ... Lúc đầu, nó có vẻ như là một sự giám sát cho Collectiongiao diện.

Lưu ý rằng "đầu tiên" sẽ không luôn trả lại điều đầu tiên bạn đặt trong bộ sưu tập và chỉ có thể có ý nghĩa đối với các bộ sưu tập được đặt hàng. Có lẽ đó là lý do tại sao không có get(item)cuộc gọi, vì đơn hàng không nhất thiết phải được bảo tồn.

Mặc dù nó có vẻ hơi lãng phí, nhưng nó có thể không tệ như bạn nghĩ. Thực Iteratorsự chỉ chứa thông tin lập chỉ mục vào bộ sưu tập, thường không phải là bản sao của toàn bộ bộ sưu tập. Gọi phương thức này sẽ khởi tạo Iteratorđối tượng, nhưng đó thực sự là chi phí duy nhất (không giống như sao chép tất cả các phần tử).

Ví dụ, nhìn vào kiểu được trả về bởi ArrayList<String>.iterator()phương thức, chúng ta thấy rằng nó là ArrayList::Itr. Đây là một lớp nội bộ chỉ truy cập trực tiếp các phần tử của danh sách, thay vì sao chép chúng.

Chỉ cần chắc chắn rằng bạn kiểm tra sự trở lại của iterator()nó vì nó có thể trống hoặc nulltùy thuộc vào việc thực hiện.


3
Điều quan trọng cần lưu ý là "mẹo" này chỉ hoạt động khi bộ sưu tập thực sự có nội dung. Nếu nó trống, trình lặp có thể trả về lỗi, trong đó người ta phải kiểm tra kích thước bộ sưu tập trước.
spaceemotion

20
Đây phải là câu trả lời chính xác. Tôi không hiểu tại sao câu trả lời luôn là "sử dụng thư viện khác!" .
Kuzeko

Làm thế nào về yếu tố thứ hai của bộ sưu tập? Tại sao đầu tiên-> next () không hoạt động? Tôi nên làm gì? Cảm ơn!
pb772

không đủ an toàn, không đảm bảo rằng bộ sưu tập sẽ luôn hướng đến yếu tố thứ nhất.
Nhà phát triển tiếp theo

84

Trong java 8:

Optional<String> firstElement = collection.stream().findFirst();

Đối với các phiên bản cũ hơn của java, có một phương thức getFirst trong Guava Iterables :

Iterables.getFirst(iterable, defaultValue)

6
Giải pháp java 8 đặc biệt hữu ích, vì nó xử lý trường hợp bộ sưu tập trống một cách duyên dáng.
SpaceTrucker 16/2/2016

4
Không tốt. Bạn thêm chi phí của luồng () để nhận get (0) chỉ vì bạn lười viết 4 dòng mã. if (! CollectionUtils.isEmpty (ProductList)) {return Options.of (productList.get (0)); } return Tùy chọn.empty ();
RS

Tôi không có getFirstsẵn phương pháp. Có getgetLastphương pháp
user1209216

4
@RS và điều gì xảy ra nếu bạn không thể gọi sản phẩmList.get (0), vì đó là Bộ sưu tập ..? (Theo câu hỏi của OP)
Denham Coote

40

Không có thứ gọi là "đầu tiên" trong một Collectionvì nó chỉ là một bộ sưu tập.

Từ phương thức Collection.iterator () của tài liệu Java :

Không có gì đảm bảo về thứ tự các phần tử được trả về ...

Vì vậy, bạn không thể.

Nếu bạn sử dụng một giao diện như Danh sách , bạn có thể làm như sau:

String first = strs.get(0);

Nhưng trực tiếp từ Bộ sưu tập này là không thể.


11
Tôi không nghĩ get(int n)là được định nghĩa choCollection
Nick Heiner

2
Bạn nói đúng, tôi nhớ điểm đó. Tôi đã cập nhật câu trả lời. Bạn không thể! (trừ khi Bộ sưu tập được triển khai bởi một số lớp lót cho phép đảm bảo)
OscarRyz

get không có trong giao diện Bộ sưu tập
Andy Gherna

21
Oscar, tôi nghĩ bạn đang nói quá. Phần tử đầu tiên của Bộ sưu tập có thể tùy ý trong một vài trường hợp như Hashset, nhưng nó được xác định rõ: đó là .iterator (). Next (). Nó cũng ổn định , trong mọi triển khai bộ sưu tập tôi từng thấy. (có liên quan: lưu ý rằng mặc dù Set không đảm bảo trật tự, nhưng mỗi kiểu con của Set trong JDK ngoại trừ Hashset cũng vậy.)
Kevin Bourrillion

3
Có thể, nhưng hãy xem xét trường hợp khi bạn thêm một yếu tố mới vào bộ sưu tập, bạn không biết (bởi giao diện) nếu yếu tố đó là đầu tiên, cuối cùng hoặc nó sẽ được chèn vào giữa. Để có kết quả chính xác, bạn nên sử dụng giao diện khác. Tuy nhiên, có lẽ những gì Rosarch cần là yếu tố đầu tiên không có vấn đề gì. Biết bộ sưu tập lót có thể giúp ích, nhưng nó ngăn bạn thay đổi nó.
OscarRyz

4

Có vẻ như Bộ sưu tập của bạn muốn giống như Danh sách, vì vậy tôi đề xuất:

List<String> myList = new ArrayList<String>();
...
String first = myList.get(0);

2

Trong Java 8, bạn có một số toán tử để sử dụng, ví dụ như giới hạn

     /**
 * Operator that limit the total number of items emitted through the pipeline
 * Shall print
 * [1]
 * @throws InterruptedException
 */
@Test
public void limitStream() throws InterruptedException {
    List<Integer> list = Arrays.asList(1, 2, 3, 1, 4, 2, 3)
                               .stream()
                               .limit(1)
                               .collect(toList());
    System.out.println(list);
}

2
@Vitalii Câu trả lời của Fedorenko stackoverflow.com/a/18165855/1562662 là tốt hơn.
Chacko Mathew

2

Quả ổi cung cấp một onlyElement Collector, nhưng chỉ sử dụng nó nếu bạn mong muốn bộ sưu tập có chính xác một yếu tố.

Collection<String> stringCollection = ...;
String string = collection.stream().collect(MoreCollectors.onlyElement())

Nếu bạn không chắc có bao nhiêu yếu tố, hãy sử dụng findFirst.

Optional<String> optionalString = collection.stream().findFirst();

1

Bạn có thể làm một buổi casting. Ví dụ: nếu tồn tại một phương thức với định nghĩa này và bạn biết rằng phương thức này đang trả về Danh sách:

Collection<String> getStrings();

Và sau khi gọi nó, bạn cần phần tử đầu tiên, bạn có thể làm như thế này:

List<String> listString = (List) getStrings();
String firstElement = (listString.isEmpty() ? null : listString.get(0));

0

Nếu bạn biết rằng bộ sưu tập là một hàng đợi thì bạn có thể chuyển bộ sưu tập thành một hàng đợi và lấy nó dễ dàng.

Có một số cấu trúc bạn có thể sử dụng để nhận đơn đặt hàng, nhưng bạn sẽ cần phải sử dụng nó.


Tôi đồng ý, nếu bạn không muốn lặp lại, đừng sử dụng bộ sưu tập. Sử dụng một số giao diện cụ thể khác thay thế.
Adeel Ansari

1
Mặc dù vậy, tôi tự hỏi ... giả sử dữ liệu cơ bản thực tế là Sắp xếp, vì vậy thứ tự có ý nghĩa, nhưng bạn chỉ có chế độ xem Bộ sưu tập về nó (vì một lý do không ngớ ngẩn, giả sử); nếu bạn bỏ Bộ sưu tập vào Danh sách, Hàng đợi, v.v. và cố gắng lấy / thăm dò ý kiến, v.v., liệu thảm họa có xảy ra không? Tương tự như vậy, nếu cấu trúc cơ bản là một Danh sách, vân vân, vân vân.
Carl

@Cal - Tôi chưa thử nó, nhưng nếu bạn chuyển một bộ sưu tập thành một loại rất khác so với ban đầu, bạn sẽ gặp lỗi, nhưng, tôi đã không thử nó, vì vậy tôi có thể sai.
James Black

0

Nó hoàn toàn phụ thuộc vào việc bạn đã sử dụng triển khai nào, cho dù danh sách liên kết danh sách mảng, hoặc các triển khai khác của tập hợp.

nếu nó được đặt thì bạn có thể trực tiếp lấy phần tử đầu tiên, chúng có thể là vòng lặp lừa trên bộ sưu tập, tạo biến có giá trị 1 và nhận giá trị khi giá trị cờ là 1 sau khi phá vỡ vòng lặp đó.

nếu đó là việc thực hiện danh sách thì thật dễ dàng bằng cách xác định số chỉ mục.


0

Cách chức năng:

public static <T> Optional<T> findFirst(List<T> result) {
    return Optional.ofNullable(result)
            .map(List::stream)
            .flatMap(Stream::findFirst);
}

đoạn mã trên bảo tồn từ NullPulumException và IndexOutOfBoundException


1
Sự lựa chọn của List<T>bạn không thỏa mãn điều kiện là nó sẽ hoạt động cho một Collection<String>, nhưng tất nhiên có thể được sửa bằng cách sử dụng Collection<T>, với sự thay đổi bổ sung : .map(Collection::stream).
Scratte

-2

Bạn có thể làm điều này:

String strz[] = strs.toArray(String[strs.size()]);
String theFirstOne = strz[0];

Javadoc cho Bộ sưu tập đưa ra thứ tự wrt caveat sau đây về các thành phần của mảng:

Nếu bộ sưu tập này đảm bảo bất kỳ thứ tự nào các phần tử của nó được trả về bởi trình lặp của nó, phương thức này phải trả về các phần tử theo cùng một thứ tự.


2
Điều này tạo ra một chuỗi String mới, tốn kém hơn nhiều so với việc tạo một trình vòng lặp.
Jim Ferrans

Vâng, tôi nghĩ về điều đó sau khi tôi đăng bài này. Bất kể phương pháp nào được sử dụng, việc đặt hàng phụ thuộc vào việc triển khai cơ bản của Bộ sưu tập. "Đầu tiên" sau đó trở thành một thuật ngữ tương đối. Tuy nhiên, cách làm iterator () có lẽ tốt hơn trong hầu hết các trường hợp.
Andy Gherna

2
Tôi đến đây vì đây là giải pháp tôi có và thấy xấu xí.
haansn08
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.