Nhận kích thước của một Lặp lại trong Java


88

Tôi cần tìm ra số phần tử Iterabletrong Java. Tôi biết tôi có thể làm điều này:

Iterable values = ...
it = values.iterator();
while (it.hasNext()) {
  it.next();
  sum++;
}

Tôi cũng có thể làm điều gì đó như thế này, vì tôi không cần các đối tượng trong Iterable nữa:

it = values.iterator();
while (it.hasNext()) {
  it.remove();
  sum++;
}

Một điểm chuẩn quy mô nhỏ không cho thấy sự khác biệt nhiều về hiệu suất, bất kỳ nhận xét hoặc ý tưởng khác cho vấn đề này?


Tại sao? Một trong hai là một ý tưởng thực sự tồi, vì cả hai đều là O (N). Bạn nên cố gắng tránh phải lặp lại bộ sưu tập hai lần.
Marquis of Lorne,

3
Cái thứ hai của bạn thậm chí không hoạt động. Bạn không thể gọi remove()mà không gọi next()trước.
Louis Wasserman,

Câu trả lời:


123

TL; DR: Sử dụng phương thức tiện ích Iterables.size(Iterable)của thư viện Guava tuyệt vời .

Trong số hai đoạn mã của bạn, bạn nên sử dụng đoạn mã đầu tiên, vì đoạn mã thứ hai sẽ xóa tất cả các phần tử khỏi đó values, vì vậy nó sẽ trống sau đó. Thay đổi cấu trúc dữ liệu cho một truy vấn đơn giản như kích thước của nó là rất bất ngờ.

Đối với hiệu suất, điều này phụ thuộc vào cấu trúc dữ liệu của bạn. Nếu nó là ví dụ trong thực tế ArrayList, việc loại bỏ các phần tử ngay từ đầu (phương pháp thứ hai của bạn đang làm) rất chậm (tính toán kích thước trở thành O (n * n) thay vì O (n) như nó phải như vậy).

Nói chung, nếu có cơ hội valuesthực sự là một Collectionvà không chỉ là một Iterable, hãy kiểm tra điều này và gọi size()trong trường hợp:

if (values instanceof Collection<?>) {
  return ((Collection<?>)values).size();
}
// use Iterator here...

Các cuộc gọi đến size()thường sẽ được nhanh hơn nhiều so với đếm số lượng các yếu tố, và thủ thuật này là chính xác những gì Iterables.size(Iterable)của Ổi làm cho bạn.


Anh ấy nói rằng anh ấy không quan tâm đến các yếu tố và không quan tâm nếu chúng bị loại bỏ.
aioobe

Làm rõ nhỏ: nó sẽ loại bỏ các yếu tố từvalues
Miquel

1
Nhưng các phần khác của mã có thể vẫn đang sử dụng cùng một Lặp lại. Và nếu không phải bây giờ, có lẽ trong tương lai.
Philipp Wendler vào

Tôi đề nghị làm cho câu trả lời Google Guava nổi bật hơn. Không có lý do gì để bắt mọi người viết lại mã này, mặc dù nó rất nhỏ.
Stephen Harrison

8
Nếu bạn sử dụng Java 8, tạo ra một Stream và đếm phần tử trong nó như: Stream.of (myIterable) .count ()
FrankBr

41

Nếu bạn đang làm việc với java 8, bạn có thể sử dụng:

Iterable values = ...
long size = values.spliterator().getExactSizeIfKnown();

nó sẽ chỉ hoạt động nếu nguồn có thể lặp lại có kích thước xác định. Hầu hết các Spliterator cho Bộ sưu tập sẽ như vậy, nhưng bạn có thể gặp vấn đề nếu nó đến từ một HashSethoặc ResultSetchẳng hạn.

Bạn có thể kiểm tra javadoc tại đây.

Nếu Java 8 không phải là một tùy chọn hoặc nếu bạn không biết có thể lặp lại đến từ đâu, bạn có thể sử dụng cách tiếp cận tương tự như ổi:

  if (iterable instanceof Collection) {
        return ((Collection<?>) iterable).size();
    } else {
        int count = 0;
        Iterator iterator = iterable.iterator();
        while(iterator.hasNext()) {
            iterator.next();
            count++;
        }
        return count;
    }

1
Ai đó, đưa cho anh chàng này một huy chương.
Pramesh Bajracharya,

Nó không hoạt động đối với tôi cho Sắp xếp . Câu trả lời của Ong Mới làm việc cho tôi.
Sabir Khan

18

Điều này có lẽ hơi muộn, nhưng có thể giúp ích cho ai đó. Tôi gặp phải vấn đề tương tự Iterabletrong cơ sở mã của mình và giải pháp là sử dụng for eachmà không cần gọi rõ ràng values.iterator();.

int size = 0;
for(T value : values) {
   size++;
}

2
Đối với tôi, đây là một cách tiếp cận trực quan, mà tôi có thể đánh giá cao. Chỉ cần sử dụng nó một vài phút trước đây. Cảm ơn.
Matt Cremeens

2
Vâng đó là trực quan, nhưng buồn thay bạn phải ngăn chặn một không sử dụng cảnh báo cho giá trị ...
Nils-o-mat

Cảm ơn giải pháp này, nó thực sự trả về kích thước của đối tượng. Mặt sau duy nhất là nếu bạn muốn lặp lại sau đó thông qua cùng một đối tượng, thì trước tiên bạn nên đặt lại nó bằng cách nào đó.
Zsolti

7

Bạn có thể truyền có thể lặp lại của mình vào một danh sách sau đó sử dụng .size () trên đó.

Lists.newArrayList(iterable).size();

Để rõ ràng, phương pháp trên sẽ yêu cầu nhập sau:

import com.google.common.collect.Lists;

2
Danh sách là một lớp Guava
Paul Jackson

6

Nói một cách chính xác, Iterable không có kích thước. Hãy nghĩ cấu trúc dữ liệu giống như một chu trình.

Và hãy nghĩ về trường hợp lặp lại sau đây, Không có kích thước:

    new Iterable(){

        @Override public Iterator iterator() {
            return new Iterator(){

                @Override
                public boolean hasNext() {
                    return isExternalSystemAvailble();
                }

                @Override
                public Object next() {
                    return fetchDataFromExternalSystem();
                }};
        }};

2

Tôi sẽ đi vì it.next()lý do đơn giản đó next()là đảm bảo được thực hiện, trong khi đó remove()là một hoạt động tùy chọn.

E next()

Trả về phần tử tiếp theo trong vòng lặp.

void remove()

Loại bỏ khỏi tập hợp cơ bản phần tử cuối cùng được trả về bởi trình vòng lặp (thao tác tùy chọn) .


Mặc dù câu trả lời này đúng về mặt kỹ thuật , nhưng nó khá sai lầm. Việc gọi removeđã được ghi nhận là sai cách để đếm các phần tử của an Iterator, vì vậy việc chúng ta không làm có được thực hiện hay không thực sự không quan trọng.
Stephen Harrison

1
Chính xác phần nào của câu trả lời là sai lệch? Và trong trường hợp remove được thực hiện, tại sao nó sẽ sai khi sử dụng nó? Btw, downvotes được sử dụng cho các câu trả lời sai hoặc câu trả lời đưa ra lời khuyên tồi. Tôi không thể thấy làm thế nào câu trả lời này đủ điều kiện cho bất kỳ điều gì trong số đó.
aioobe

2

java 8 trở lên

StreamSupport.stream(data.spliterator(), false).count();

0

Đối với tôi, đây chỉ là những phương pháp khác nhau. Cái đầu tiên để đối tượng mà bạn đang lặp lại không thay đổi, trong khi giây để trống. Câu hỏi là bạn muốn làm gì. Sự phức tạp của việc xóa dựa trên việc triển khai đối tượng có thể lặp lại của bạn. Nếu bạn đang sử dụng Bộ sưu tập - chỉ cần lấy kích thước như đã được đề xuất bởi Kazekage Gaara - đó thường là cách tiếp cận hiệu quả tốt nhất.


-2

Tại sao bạn không đơn giản sử dụng size()phương pháp trên của bạn Collectionđể lấy số phần tử?

Iterator chỉ có nghĩa là lặp lại, không có gì khác.


4
Ai nói rằng anh ấy đang sử dụng một bộ sưu tập? :-)
aioobe

Bạn đang sử dụng trình lặp, hỗ trợ loại bỏ các phần tử. Nếu bạn chọn kích thước trước khi vào vòng lặp trình lặp, bạn cần phải cẩn thận với các lần xóa của mình và cập nhật giá trị cho phù hợp.
Miquel

1
Một ví dụ về lý do tại sao anh ta có thể không có một bộ sưu tập để xem xét kích thước: anh ta có thể đang gọi một phương thức API của bên thứ ba chỉ trả về một Lặp lại.
SteveT

2
Câu trả lời này gây nhầm lẫn IteratorIterable. Xem câu trả lời được bình chọn để biết cách chính xác.
Stephen Harrison

-4

Thay vì sử dụng các vòng lặp và đếm từng phần tử hoặc sử dụng và thư viện của bên thứ ba, chúng ta có thể chỉ cần nhập tệp có thể lặp lại trong ArrayList và lấy kích thước của nó.

((ArrayList) iterable).size();

3
Không phải tất cả iterables có thể được đúc để ArrayList tho
Alexander Mills
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.