รับขนาดของการทำซ้ำใน Java


90

ฉันต้องการหาจำนวนองค์ประกอบIterableใน Java ฉันรู้ว่าฉันทำได้:

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

ฉันสามารถทำอะไรแบบนี้ได้เพราะฉันไม่ต้องการวัตถุใน Iterable อีกต่อไป:

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

เกณฑ์มาตรฐานขนาดเล็กไม่ได้แสดงความแตกต่างของประสิทธิภาพมากนักความคิดเห็นหรือแนวคิดอื่น ๆ สำหรับปัญหานี้หรือไม่?


ทำไม? เป็นความคิดที่แย่จริงๆเนื่องจากทั้งคู่เป็น O (N) คุณควรพยายามหลีกเลี่ยงไม่ต้องวนซ้ำคอลเลกชันซ้ำสองครั้ง
user207421

3
อันที่สองของคุณไม่ควรทำงานด้วยซ้ำ คุณไม่สามารถโทรได้remove()โดยไม่ต้องโทรnext()ล่วงหน้า
Louis Wasserman

คำตอบ:


125

TL; DR: ใช้วิธียูทิลิตี้Iterables.size(Iterable)ของห้องสมุดGuavaที่ยอดเยี่ยม

จากสองข้อมูลโค้ดของคุณคุณควรใช้อันแรกเนื่องจากอันที่สองจะลบองค์ประกอบทั้งหมดออกvaluesดังนั้นจึงว่างเปล่าในภายหลัง การเปลี่ยนโครงสร้างข้อมูลสำหรับคิวรีธรรมดาเช่นขนาดเป็นเรื่องที่คาดไม่ถึง

สำหรับประสิทธิภาพนั้นขึ้นอยู่กับโครงสร้างข้อมูลของคุณ หากเป็นเช่นนั้นจริง ๆArrayListแล้วการลบองค์ประกอบออกจากจุดเริ่มต้น (วิธีที่สองของคุณกำลังทำอยู่) จะช้ามาก (การคำนวณขนาดจะกลายเป็น O (n * n) แทนที่จะเป็น O (n) ตามที่ควรจะเป็น)

โดยทั่วไปถ้ามีโอกาสที่valuesเป็นจริงCollectionและไม่เพียง แต่Iterableตรวจสอบนี้และเรียกร้องsize()ในกรณี:

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

เรียกร้องให้size()จะมักจะเร็วกว่าการนับจำนวนขององค์ประกอบและเคล็ดลับนี้เป็นสิ่งที่Iterables.size(Iterable)ของฝรั่งไม่สำหรับคุณ


เขาบอกว่าเขาไม่สนใจองค์ประกอบและไม่สนใจว่าจะถูกลบออกไปหรือไม่
aioobe

คำชี้แจงเล็กน้อย: จะลบองค์ประกอบออกจากvalues
Miquel

1
แต่ส่วนอื่น ๆ ของโค้ดอาจยังคงใช้ Iterable เหมือนเดิม และถ้าไม่ใช่ตอนนี้บางทีในอนาคต
Philipp Wendler

ฉันขอแนะนำให้ทำให้คำตอบของ Google Guava โดดเด่นมากขึ้น ไม่มีเหตุผลใดที่จะทำให้ผู้คนเขียนโค้ดนี้อีกแม้ว่าจะเป็นเรื่องเล็กน้อยก็ตาม
Stephen Harrison

8
หากคุณใช้ Java 8 ให้สร้างสตรีมและนับองค์ประกอบในนั้นเป็น: Stream.of (myIterable) .count ()
FrankBr

41

หากคุณกำลังทำงานกับ java 8 คุณสามารถใช้:

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

จะใช้งานได้ก็ต่อเมื่อแหล่งที่มาที่ทำซ้ำได้มีขนาดที่กำหนด Spliterators สำหรับ Collections ส่วนใหญ่จะทำได้ แต่คุณอาจมีปัญหาหากมาจาก a HashSetหรือResultSetตัวอย่างเช่น

คุณสามารถตรวจสอบjavadoc ได้ที่นี่

หาก Java 8 ไม่ใช่ตัวเลือกหรือหากคุณไม่ทราบว่าสามารถทำซ้ำได้จากที่ใดคุณสามารถใช้แนวทางเดียวกับฝรั่ง:

  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
ใครก็ได้มอบเหรียญให้ผู้ชายคนนี้
Pramesh Bajracharya

มันไม่ทำงานสำหรับฉันเรียง คำตอบโดย New Bee ใช้ได้กับฉัน
Sabir Khan

18

อาจจะช้าไปหน่อย แต่อาจช่วยใครบางคนได้ ผมเจอปัญหาที่คล้ายกันกับIterableใน codebase และวิธีการแก้ปัญหาของฉันคือการใช้โดยไม่ต้องโทรอย่างชัดเจนfor eachvalues.iterator();

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

2
นี่คือวิธีการที่ใช้งานง่ายสำหรับฉันซึ่งฉันสามารถชื่นชมได้ เพิ่งใช้เมื่อไม่กี่นาทีที่ผ่านมา ขอบคุณ.
Matt Cremeens

2
ใช่มันใช้งานง่าย แต่น่าเศร้าที่คุณต้องระงับคำเตือนที่ไม่ได้ใช้สำหรับค่า ...
Nils-o-mat

ขอบคุณสำหรับวิธีแก้ปัญหานี้ซึ่งจะคืนค่าขนาดของวัตถุ ด้านหลังเพียงอย่างเดียวคือถ้าคุณต้องการทำซ้ำอีกครั้งในภายหลังผ่านวัตถุเดียวกันก่อนอื่นคุณควรรีเซ็ตเป็นอย่างใด
Zsolti

6

พูดอย่างเคร่งครัด Iterable ไม่มีขนาด คิดโครงสร้างข้อมูลเหมือนวัฏจักร

และคิดเกี่ยวกับการติดตามอินสแตนซ์ Iterable No size:

    new Iterable(){

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

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

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

6

คุณสามารถทำซ้ำไปยังรายการจากนั้นใช้. ขนาด () กับมัน

Lists.newArrayList(iterable).size();

เพื่อความชัดเจนวิธีการข้างต้นจะต้องมีการนำเข้าดังต่อไปนี้:

import com.google.common.collect.Lists;

3
ลิสต์เป็นคลาสฝรั่ง
Paul Jackson

2

ฉันจะไปit.next()ด้วยเหตุผลง่ายๆที่next()รับประกันว่าจะใช้งานได้ในขณะที่remove()เป็นการดำเนินการที่เป็นทางเลือก

E next()

ส่งคืนองค์ประกอบถัดไปในการวนซ้ำ

void remove()

นำออกจากคอลเลกชันพื้นฐานองค์ประกอบสุดท้ายที่ส่งกลับโดยการทำซ้ำโดย(การทำงานไม่จำเป็น)


แม้ว่าคำตอบนี้จะถูกต้องในทางเทคนิคแต่ก็ค่อนข้างทำให้เข้าใจผิด การโทรremoveถูกบันทึกไว้แล้วว่าเป็นวิธีที่ผิดในการนับองค์ประกอบของ an Iteratorดังนั้นจึงไม่สำคัญว่าสิ่งที่เราจะไม่ทำนั้นจะถูกนำไปใช้หรือไม่
Stephen Harrison

1
ส่วนใดของคำตอบที่ทำให้เข้าใจผิด และภายใต้สถานการณ์ที่remove ถูกนำไปใช้ทำไมจึงจะใช้มันผิด? Btw, downvotes เป็นรหัสที่ใช้สำหรับคำตอบที่ผิดหรือคำตอบที่ให้คำแนะนำที่ไม่ดี ฉันไม่เห็นว่าคำตอบนี้มีคุณสมบัติอย่างไร
aioobe


0

สำหรับฉันนี่เป็นเพียงวิธีการที่แตกต่างกัน อันแรกปล่อยให้วัตถุที่คุณกำลังทำซ้ำไม่เปลี่ยนแปลงในขณะที่วินาทีปล่อยให้มันว่าง คำถามคือคุณอยากทำอะไร ความซับซ้อนของการลบขึ้นอยู่กับการใช้งานวัตถุที่ทำซ้ำได้ของคุณ หากคุณใช้คอลเลคชัน - เพียงแค่ได้รับขนาดตามที่ Kazekage Gaara เสนอ - โดยปกติแล้วจะเป็นวิธีการที่ดีที่สุด


-2

ทำไมคุณไม่ใช้size()วิธีการCollectionเพื่อรับจำนวนองค์ประกอบ?

Iterator มีไว้เพื่อทำซ้ำไม่มีอะไรอื่น


4
ใครบอกว่าเขาใช้คอลเลกชัน? :-)
aioobe

คุณกำลังใช้ตัววนซ้ำซึ่งรองรับการลบองค์ประกอบ หากคุณเลือกขนาดก่อนที่จะเข้าสู่วนรอบตัววนซ้ำคุณต้องระมัดระวังในการลบและอัปเดตค่าตามนั้น
Miquel

1
ตัวอย่างของสาเหตุที่เขาอาจไม่มีคอลเล็กชันสำหรับขนาด: เขาสามารถเรียกใช้เมธอด API ของบุคคลที่สามที่ส่งคืนเฉพาะ Iterable
SteveT

2
คำตอบนี้สับสนและIterator Iterableดูคำตอบที่ได้รับการโหวตสำหรับวิธีที่ถูกต้อง
Stephen Harrison

-4

แทนที่จะใช้ลูปและนับแต่ละองค์ประกอบหรือใช้และไลบรารีของบุคคลที่สามเราสามารถพิมพ์ข้อความซ้ำได้ใน ArrayList และรับขนาดของมัน

((ArrayList) iterable).size();

3
ไม่ใช่ทุกรายการที่สามารถส่งไปยัง ArrayList ได้
Alexander Mills
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.