Cách tốt nhất để lấy số lượng / chiều dài / kích thước của một trình vòng lặp là gì?


96

Có một cách nhanh chóng "tính toán" để có được số lượng của một trình lặp không?

int i = 0;
for ( ; some_iterator.hasNext() ; ++i ) some_iterator.next();

... có vẻ như lãng phí chu kỳ CPU.


2
Một trình lặp không nhất thiết phải tương ứng với một cái gì đó có "số đếm" ...
Oliver Charlesworth

Trình lặp là những gì họ đang có; để lặp tới đối tượng tiếp theo của tập hợp (nó có thể là bất kỳ thứ gì như set, array, v.v.) Tại sao họ cần cho biết kích thước khi họ không quan tâm đến việc họ đang cố gắng lặp lại để làm gì? to provide an implementation-independent method for access, in which the user does not need to know whether the underlying implementation is some form of array or of linked list, and allows the user go through the collection without explicit indexing. penguin.ewu.edu/~trolfe/LinkedSort/Iterator.html
ecle

Câu trả lời:


67

Nếu bạn vừa có trình lặp thì đó là những gì bạn sẽ phải làm - nó không biết còn lại bao nhiêu mục để lặp lại, vì vậy bạn không thể truy vấn nó cho kết quả đó. Có những phương thức tiện ích dường như sẽ thực hiện điều này (chẳng hạn như Iterators.size()trong Guava), nhưng bên dưới chúng chỉ thực hiện cùng một thao tác.

Tuy nhiên, nhiều trình vòng lặp đến từ các tập hợp, bạn thường có thể truy vấn kích thước của chúng. Và nếu đó là lớp do người dùng tạo mà bạn đang nhận trình vòng lặp, bạn có thể tìm cách cung cấp phương thức size () trên lớp đó.

Tóm lại, trong trường hợp bạn chỉ có trình lặp thì không có cách nào tốt hơn, nhưng thường xuyên hơn là bạn không có quyền truy cập vào bộ sưu tập hoặc đối tượng cơ bản mà từ đó bạn có thể lấy kích thước trực tiếp.


Hãy cẩn thận với tác dụng phụ của Iterators.size(...)(được đề cập trong các nhận xét khác bên dưới và trong java-doc): "Trả về số phần tử còn lại trong trình lặp. Trình lặp sẽ bị cạn kiệt: phương thức hasNext () của nó sẽ trả về false." Điều đó có nghĩa là sau đó bạn không thể sử dụng Iterator nữa. Lists.newArrayList(some_iterator);có thể giúp.
MichaelCkr

91

Sử dụng thư viện Guava :

int size = Iterators.size(iterator);

Bên trong nó chỉ lặp lại trên tất cả các phần tử vì vậy nó chỉ để thuận tiện.


8
Điều này rất thanh lịch. Chỉ cần nhớ rằng bạn đang sử dụng trình lặp của mình (tức là sau đó trình lặp sẽ trống)
lolski

1
Đây không phải là "nhanh chóng về mặt tính toán", đây là một phương pháp tiện lợi có tác dụng phụ không mong muốn là sử dụng trình lặp.
Zak

Bạn có thể vui lòng giải thích cách làm việc này không? @Andrejs List <Tuple2 <String, Integer >> wordCountsWithGroupByKey = wordsPairRdd.groupByKey () .mapValues ​​(intIterable -> Iterables.size (intIterable)). Collect (); System.out.println ("wordCountsWithGroupByKey:" + wordCountsWithGroupByKey); "Iterables.size (intIterable)?
Aditya Verma

15

Mã của bạn sẽ cung cấp cho bạn một ngoại lệ khi bạn đến cuối trình lặp. Bạn có thể làm:

int i = 0;
while(iterator.hasNext()) {
    i++;
    iterator.next();
}

Nếu bạn có quyền truy cập vào bộ sưu tập cơ bản, bạn sẽ có thể gọi coll.size()...

CHỈNH SỬA OK, bạn đã sửa đổi ...


làm thế nào hiệu quả là điều này? điều gì sẽ xảy ra nếu trình lặp giống như một triệu giá trị?
Micro

4
@Micro về mặt kỹ thuật, một trình lặp có thể là vô hạn - trong trường hợp đó, vòng lặp sẽ kéo dài mãi mãi.
assylias

11

Bạn sẽ luôn phải lặp lại. Tuy nhiên, bạn có thể sử dụng Java 8, 9 để thực hiện việc đếm mà không lặp lại một cách rõ ràng:

Iterable<Integer> newIterable = () -> iter;
long count = StreamSupport.stream(newIterable.spliterator(), false).count();

Đây là một bài kiểm tra:

public static void main(String[] args) throws IOException {
    Iterator<Integer> iter = Arrays.asList(1, 2, 3, 4, 5).iterator();
    Iterable<Integer> newIterable = () -> iter;
    long count = StreamSupport.stream(newIterable.spliterator(), false).count();
    System.out.println(count);
}

Bản in này:

5

Đủ thú vị, bạn có thể song song hoạt động đếm ở đây bằng cách thay đổi parallelcờ trong cuộc gọi này:

long count = StreamSupport.stream(newIterable.spliterator(), *true*).count();

8

Sử dụng thư viện Guava , một tùy chọn khác là chuyển đổi Iterablethành a List.

List list = Lists.newArrayList(some_iterator);
int count = list.size();

Sử dụng điều này nếu bạn cũng cần truy cập các phần tử của trình vòng lặp sau khi nhận được kích thước của nó. Bằng cách sử dụng, Iterators.size()bạn không còn có thể truy cập các phần tử được lặp lại.


2
@LoveToCode Kém hiệu quả hơn ví dụ trong câu hỏi ban đầu
Winter

2
Chắc chắn, việc tạo một đối tượng mới với tất cả các phần tử sẽ chậm hơn so với việc chỉ lặp lại và loại bỏ. IMHO, giải pháp này là một lớp lót giúp cải thiện khả năng đọc mã. Tôi sử dụng nó rất nhiều cho các bộ sưu tập có ít phần tử (lên đến 1000) hoặc khi tốc độ không phải là vấn đề.
tashuhka 21/12/16

7

Nếu tất cả những gì bạn có là trình lặp, thì không, không có cách nào "tốt hơn". Nếu trình lặp đến từ một tập hợp, bạn có thể làm như vậy cho kích thước.

Hãy nhớ rằng Iterator chỉ là một giao diện để duyệt các giá trị riêng biệt, bạn sẽ rất có thể có mã như thế này

    new Iterator<Long>() {
        final Random r = new Random();
        @Override
        public boolean hasNext() {
            return true;
        }

        @Override
        public Long next() {
            return r.nextLong();
        }

        @Override
        public void remove() {
            throw new IllegalArgumentException("Not implemented");
        }
    };

hoặc là

    new Iterator<BigInteger>() {
        BigInteger next = BigInteger.ZERO;

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

        @Override
        public BigInteger next() {
            BigInteger current = next;
            next = next.add(BigInteger.ONE);
            return current;
        }

        @Override
        public void remove() {
            throw new IllegalArgumentException("Not implemented");
        }
    }; 

4

Không có cách nào hiệu quả hơn, nếu tất cả những gì bạn có là trình lặp. Và nếu trình lặp chỉ có thể được sử dụng một lần, thì việc đếm số trước khi bạn nhận được nội dung của trình lặp là ... có vấn đề.

Giải pháp là thay đổi ứng dụng của bạn để ứng dụng không cần số lượng hoặc lấy số lượng bằng một số phương tiện khác. (Ví dụ: chuyển một Collectionthay vì Iterator...)


0

cho Java 8 bạn có thể sử dụng,

public static int getIteratorSize(Iterator iterator){
        AtomicInteger count = new AtomicInteger(0);
        iterator.forEachRemaining(element -> {
            count.incrementAndGet();
        });
        return count.get();
    }

-5

đối tượng vòng lặp chứa cùng một số phần tử mà bộ sưu tập của bạn chứa.

List<E> a =...;
Iterator<E> i = a.iterator();
int size = a.size();//Because iterators size is equal to list a's size.

Nhưng thay vì lấy kích thước của trình lặp và lặp qua chỉ mục 0 đến kích thước đó, tốt hơn nên lặp qua phương thức next () của trình lặp.


Điều gì sẽ xảy ra nếu chúng ta không có a, nhưng chỉ có i?
Tvde1
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.