Bir yineleyicinin sayısını / uzunluğunu / boyutunu almanın en iyi yolu nedir?


100

Yineleyicinin sayısını almanın "sayısal olarak" hızlı bir yolu var mı?

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

... CPU döngülerinin boşa harcanması gibi görünüyor.


2
Yineleyici, "sayı" olan bir şeye karşılık gelmek zorunda değildir ...
Oliver Charlesworth

Yineleyiciler ne olduklarıdır; bir koleksiyonun sonraki nesnesine yinelemek için (set, dizi vb. herhangi bir şey olabilir) Neyi yinelemeye çalıştıkları umurlarında değilken neden boyutu söylemeleri gerekiyor? 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
ekle

Yanıtlar:


67

Yineleyiciyi yeni aldıysanız, yapmanız gereken şey budur - yinelemek için kaç öğe kaldığını bilmez , bu nedenle bu sonucu sorgulayamazsınız. Bunu yapacak gibi görünen yardımcı yöntemler vardır ( Iterators.size()Guava'da olduğu gibi ), ancak bunların altında yaklaşık olarak aynı işlemi gerçekleştiriyorlar.

Ancak birçok yineleyici, genellikle boyutlarını sorgulayabileceğiniz koleksiyonlardan gelir. Yineleyiciyi aldığınız kullanıcı tarafından oluşturulmuş bir sınıfsa, bu sınıfta bir size () yöntemi sağlamaya çalışabilirsiniz.

Kısacası, yalnızca yineleyiciye sahip olduğunuz durumda daha iyi bir yol yoktur, ancak boyutu doğrudan alabileceğiniz temel koleksiyona veya nesneye erişebilmenizden çok daha fazlası yoktur.


Şu yan etkiye dikkat edin ( Iterators.size(...)aşağıdaki diğer yorumlarda ve java-doc'ta bahsedilmiştir): "Yineleyicide kalan öğelerin sayısını döndürür. Yineleyici bitmiş olarak bırakılır: hasNext () yöntemi yanlış döndürür." Bu, daha sonra Yineleyiciyi kullanamayacağınız anlamına gelir. Lists.newArrayList(some_iterator);yardımcı olabilir.
MichaelCkr

91

Guava kitaplığını kullanarak :

int size = Iterators.size(iterator);

Dahili olarak, sadece kolaylık sağlamak için tüm öğeleri yineler.


8
Bu çok zarif. Yineleyicinizi tükettiğinizi unutmayın (yani yineleyici daha sonra boş olacaktır)
lolski

1
Bu, "hesaplama açısından hızlı" değildir, bu, yineleyiciyi tüketmenin istenmeyen yan etkisine sahip uygun bir yöntemdir.
Zak

Bunun nasıl çalıştığını açıklar mısınız lütfen? @Andrejs Listesi <Tuple2 <String, Integer >> wordCountsWithGroupByKey = wordsPairRdd.groupByKey () .mapValues ​​(intIterable -> Iterables.size (intIterable)). Collect (); System.out.println ("wordCountsWithGroupByKey:" + wordCountsWithGroupByKey); "Yinelenebilir boyut (değiştirilebilir)?
Aditya Verma

15

Kodunuz, yineleyicinin sonuna geldiğinizde size bir istisna verecektir. Yapabilirsin:

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

Altta yatan koleksiyona erişiminiz olsaydı, arayabilirdiniz coll.size()...

DÜZENLE TAMAM, değiştirdiniz ...


bu ne kadar verimli? Ya yineleyici bir milyon değer gibiyse?
Micro

4
@Micro teknik olarak bir yineleyici sonsuz olabilir - bu durumda döngü sonsuza kadar devam eder.
assylias

12

Her zaman yinelemeniz gerekecek. Yine de sayımı açıkça döngü yapmadan yapmak için Java 8, 9'u kullanabilirsiniz:

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

İşte bir test:

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);
}

Bu şunu yazdırır:

5

Yeterince ilginç olan parallel, bu çağrıdaki bayrağı değiştirerek burada sayma işlemini paralel hale getirebilirsiniz :

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

8

Guava kitaplığını kullanarak , başka bir seçenek, Iterablebir List.

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

Boyutunu aldıktan sonra yineleyicinin öğelerine de erişmeniz gerekiyorsa bunu kullanın. Iterators.size()Sizi kullanarak yinelenen öğelere artık erişemezsiniz.


2
@LoveToCode Orijinal sorudaki örnekten daha az verimli
Kış

2
Elbette, tüm öğelerle yeni bir nesne oluşturmak, yinelemekten ve atmaktan daha yavaştır. IMHO, bu çözüm, kod okunabilirliğini artıran tek satırlık bir çözümdür. Az öğeli (1000'e kadar) koleksiyonlar için veya hızın sorun olmadığı durumlarda çok kullanıyorum.
tashuhka

7

Sahip olduğunuz tek şey yineleyici ise, o zaman hayır, "daha iyi" bir yol yoktur. Yineleyici bir koleksiyondan geliyorsa, boyut için bunu yapabilirsiniz.

Yineleyicinin yalnızca farklı değerleri geçmek için bir arayüz olduğunu unutmayın, bunun gibi bir koda sahip olursunuz.

    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");
        }
    };

veya

    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

Sahip olduğunuz tek şey yineleyiciyse, daha verimli bir yol yoktur. Ve yineleyici yalnızca bir kez kullanılabiliyorsa, yineleyicinin içeriğini almadan önce sayımı almak ... sorunludur.

Çözüm, sayıma ihtiyaç duymaması için uygulamanızı değiştirmek veya sayımı başka yollarla elde etmektir. (Örneğin, ... Collectionyerine a iletin Iterator)


0

Java 8 için kullanabilirsiniz,

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

-6

yineleyici nesnesi, koleksiyonunuzun içerdiği aynı sayıda öğeyi içerir.

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

Ancak yineleyicinin boyutunu almak ve dizin 0 aracılığıyla bu boyuta yinelemek yerine, yineleyicinin next () yöntemini yinelemek daha iyidir .


1
Ya sahip değilsek a, ama sadece i?
Tvde1
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.