เหตุใดอาร์เรย์จึงไม่สามารถกำหนดให้กับ Iterable ได้


186

ด้วย Java5 เราสามารถเขียน:

Foo[] foos = ...
for (Foo foo : foos) 

หรือเพียงแค่ใช้ Iterable ใน for for loop มันมีประโยชน์มาก

อย่างไรก็ตามคุณไม่สามารถเขียนวิธีทั่วไปสำหรับการทำซ้ำเช่นนี้:

public void bar(Iterable<Foo> foos) { .. }

และเรียกมันด้วยอาเรย์เนื่องจากมันไม่ใช่ Iterable:

Foo[] foos = { .. };
bar(foos);  // compile time error 

ฉันสงสัยเกี่ยวกับเหตุผลที่อยู่เบื้องหลังการตัดสินใจออกแบบนี้


8
Arrays.asList ดีพอที่ฉันคิดว่า
dfa

17
มันเป็นคำถามปรัชญา
dfa

2
เหตุผลที่ดีในการจัดการกับอาร์เรย์ใน Java 5+ เป็นวิธีการแปรผัน
Jeff Walker

2
@Torsten: จริง แต่ถ้าคุณส่งผ่านไปยังวิธีการที่ยอมรับ Iterable คุณอาจไม่ทำการเปลี่ยนแปลงใด ๆ
Michael Myers

5
ที่จริงแล้ว Arrays.asList นั้นไม่ดีพอเพราะมันไม่สามารถใช้งานกับอาร์เรย์ของประเภทดั้งเดิมได้ วิธีเดียวที่มีอยู่แล้วภายในในการทำซ้ำองค์ประกอบ (ชนิดบรรจุกล่อง) แบบดั้งเดิมโดยใช้การสะท้อนการใช้java.lang.reflect.Arrayแต่ประสิทธิภาพการทำงานจะอ่อนแอ อย่างไรก็ตามคุณสามารถเขียนตัววนซ้ำของคุณเอง (หรือใช้งานรายการ!) เพื่อตัดอาร์เรย์ประเภทดั้งเดิมหากคุณต้องการ
Boann

คำตอบ:


78

อาร์เรย์สามารถใช้อินเตอร์เฟส ( Cloneableและjava.io.Serializable) ดังนั้นทำไมไม่Iterable? ฉันเดาว่าIterableกำลังเพิ่มiteratorวิธีการและอาร์เรย์ไม่ใช้วิธีการ ไม่ได้แทนที่char[] toStringอย่างไรก็ตามอาร์เรย์ของข้อมูลอ้างอิงที่ควรได้รับการพิจารณาน้อยกว่าเหมาะ - ใช้Lists ในฐานะที่เป็นความคิดเห็น dfa Arrays.asListจะทำการแปลงให้คุณอย่างชัดเจน

(ต้องบอกว่าคุณสามารถโทรหาcloneอาร์เรย์ได้)


23
> "... และอาร์เรย์ไม่ใช้วิธีการ" ฉันคิดว่านี่เป็นคำถามเชิงปรัชญาอีกข้อหนึ่ง อาร์เรย์ไม่เคยเป็นชนิดดั้งเดิมและปรัชญาของจาวาอ่านว่า "ทุกอย่างเป็นวัตถุ (ยกเว้นประเภทดั้งเดิม)" เหตุใดจึงทำอาร์เรย์ไม่ใช้วิธีการแม้ว่าจะมีการดำเนินการ gazillion ที่ใคร ๆ ก็อยากจะใช้อาเรย์ตั้งแต่เริ่มต้น โอ้ใช่แล้วอาร์เรย์เป็นคอลเลกชันที่พิมพ์อย่างเดียวเท่านั้นก่อนที่นายพลจะมารวมกันเป็นปัญหาหลังค่อม
fatuhoku

2
หากคุณมีข้อมูลในอาเรย์เป็นไปได้ว่าคุณกำลังทำงานในระดับต่ำและมีผลงานสำคัญเช่นการจัดการกับไบต์ของการอ่านจากสตรีม การไม่สามารถทำซ้ำอาร์เรย์อาจเกิดจาก Java generics ที่ไม่สนับสนุน primitives เป็นอาร์กิวเมนต์ชนิดเช่น @Gareth พูดด้านล่าง
Drew Noakes

2
@FatuHoku คำแนะนำทั่วไปมีปัญหาย้อนหลังที่ไม่ถูกต้อง ความปรารถนาของยาชื่อสามัญได้รับการชื่นชมเสมอ อาร์เรย์ไม่ใช่แบบดั้งเดิม (ฉันไม่ได้บอกว่ามันเป็น) แต่มันอยู่ในระดับต่ำ สิ่งหนึ่งที่คุณต้องการทำกับอาร์เรย์คือใช้พวกเขาเป็นรายละเอียดการใช้งานสำหรับโครงสร้างแบบเวกเตอร์
Tom Hawtin - tackline

1
Iterator<T>นอกจากนี้ยังต้องถึงแม้ว่ามันจะได้รับอนุญาตให้โยนremove(T) UnsupportedOperationException
wchargin

java.lang.Objectอาร์เรย์จะใช้วิธีการที่พวกเขาใช้วิธีการทั้งหมดของ
ช่างตีเหล็ก

59

อาร์เรย์เป็นวัตถุ แต่รายการของมันอาจไม่เป็นเช่นนั้น อาร์เรย์อาจมีประเภทดั้งเดิมเช่น int ซึ่ง Iterable ไม่สามารถรับมือได้ อย่างน้อยนั่นคือสิ่งที่ฉันคิด


3
ซึ่งหมายความว่าเพื่อรองรับIterableอินเทอร์เฟซอาร์เรย์ดั้งเดิมจะต้องมีความเชี่ยวชาญในการใช้คลาส wrapper สิ่งนี้ไม่ได้เป็นเรื่องใหญ่ แต่อย่างใดเนื่องจากพารามิเตอร์ประเภทล้วนเป็นของปลอมอยู่ดี
thejoshwolfe

8
สิ่งนี้จะไม่ป้องกัน Object Array ไม่ให้ใช้ Iterable มันจะไม่ป้องกันอาร์เรย์ดั้งเดิมจากการใช้ Iterable สำหรับชนิดที่ถูกห่อด้วย
Boann

1
Autoboxing สามารถจัดการสิ่งนี้ได้
Tim Büthe

ฉันคิดว่านี่เป็นเหตุผลที่ถูกต้อง มันจะไม่ทำงานที่น่าพอใจสำหรับอาร์เรย์ประเภทดั้งเดิมจนกว่า Generics จะสนับสนุนประเภทดั้งเดิม (เช่นList<int>แทนList<Integer>ฯลฯ ) สับสามารถทำได้ด้วยห่อ แต่ที่สูญเสียประสิทธิภาพการทำงาน - และที่สำคัญกว่า - ถ้านี้ตัดเสร็จ it'ld ป้องกันไม่ให้การดำเนินการอย่างถูกต้องในชวาในอนาคต (เช่นint[].iterator()จะถูกล็อคตลอดไปจะกลับมาIterator<Integer>มากกว่าIterator<int>) บางทีอาจจะเป็นมูลค่าประเภทที่จะเกิดขึ้น + ทั่วไป-เชี่ยวชาญสำหรับ Java (โครงการ Valhalla) Iterableจะทำให้การดำเนินการอาร์เรย์
Bjarke

16

อาเรย์ควรให้การสนับสนุนIterableพวกเขาก็ทำไม่ได้ด้วยเหตุผลเดียวกับที่อาเรย์. NET ไม่สนับสนุนอินเทอร์เฟซที่อนุญาตให้เข้าถึงแบบสุ่มโดยตำแหน่งแบบอ่านอย่างเดียว โดยทั่วไปกรอบมักจะมีช่องว่างเล็ก ๆ น้อย ๆ ที่น่ารำคาญซึ่งมันไม่คุ้มค่ากับเวลาที่จะแก้ไข มันจะไม่สำคัญหากเราสามารถแก้ไขได้ด้วยตนเองในวิธีที่เหมาะสมที่สุด แต่บ่อยครั้งที่เราทำไม่ได้

UPDATE:เพื่อเป็นมือเดียวฉันได้กล่าวถึงอาร์เรย์. NET ที่ไม่สนับสนุนอินเทอร์เฟซที่รองรับการเข้าถึงแบบสุ่มตามตำแหน่ง (ดูความคิดเห็นของฉันด้วย) แต่ใน. NET 4.5 นั้นอินเทอร์เฟซที่แน่นอนได้ถูกกำหนดและได้รับการสนับสนุนโดยอาร์เรย์และList<T>คลาส:

IReadOnlyList<int> a = new[] {1, 2, 3, 4};
IReadOnlyList<int> b = new List<int> { 1, 2, 3, 4 };

ทั้งหมดยังไม่สมบูรณ์แบบเนื่องจากส่วนติดต่อรายการที่ไม่แน่นอนIList<T>ไม่ได้รับIReadOnlyList<T>:

IList<int> c = new List<int> { 1, 2, 3, 4 };
IReadOnlyList<int> d = c; // error

บางทีอาจมีความเป็นไปได้ในการใช้งานร่วมกับ gotcha แบบย้อนหลังได้เช่นกัน

หากมีความคืบหน้าเกี่ยวกับสิ่งที่คล้ายกันใน Java เวอร์ชันใหม่กว่าฉันสนใจที่จะทราบในความคิดเห็น! :)


8
.NET อาร์เรย์ใช้อินเทอร์เฟซ IList
Tom Gillen

2
@Aphid - ฉันว่าการเข้าถึงแบบสุ่มอ่านอย่างเดียว IList<T>exposes การดำเนินงานสำหรับการปรับเปลี่ยน มันจะดีถ้าIList<T>มีอะไรบางอย่างที่สืบทอดมาเช่นIReadonlyList<T>อินเตอร์เฟซซึ่งจะมีเพียงแค่มีCountและT this[int]สืบทอดIEnumerable<T>(ซึ่งแล้วสนับสนุนการแจงนับอ่านอย่างเดียว) อีกสิ่งที่ดีจะเป็นอินเตอร์เฟซสำหรับการได้รับการแจงนับย้อนกลับการสั่งซื้อซึ่งReverseวิธีขยายสามารถสอบถาม (เช่นเดียวกับCountคำสั่งวิธีการขยายICollectionเพื่อเพิ่มประสิทธิภาพของตัวเอง.)
แดเนียล Earwicker

ใช่มันจะดีกว่ามากถ้าสิ่งต่าง ๆ ได้รับการออกแบบในลักษณะนั้น ส่วนต่อประสาน IList จะกำหนดคุณสมบัติ IsReadOnly และ IsFixedSize ซึ่งมีการใช้งานอย่างเหมาะสมโดยอาร์เรย์ ที่ทำให้ฉันเป็นวิธีที่แย่มากในการทำเช่นนี้เนื่องจากไม่มีการรวบรวมเวลาตรวจสอบว่ารายการที่คุณได้รับนั้นเป็นเรื่องที่อ่านได้ง่ายและฉันไม่ค่อยเห็นรหัสที่ตรวจสอบคุณสมบัติเหล่านี้
Tom Gillen

1
อาร์เรย์ใน. NET ใช้งานIListและICollectionตั้งแต่. NET 1.1 IList<T>และICollection<T>ตั้งแต่. NET 2.0 นี่เป็นอีกกรณีหนึ่งที่ Java อยู่ไกลจากการแข่งขัน
Amir Abiri

@TomGillen: ปัญหาที่ใหญ่ที่สุดของฉันIListคือมันไม่ได้มีคุณลักษณะที่น่าสงสัยเพิ่มเติม ฉันว่าชุดที่เหมาะสมควรมี IsUpdateable, IsResizable, IsReadOnly, IsFixedSize และ ExistingElementsAreImmutable สำหรับ starters คำถามที่ว่าการอ้างอิงรหัสสามารถทำได้หรือไม่โดยไม่ต้องพิมพ์การดัดแปลงรายการจะแยกจากคำถามว่ารหัสที่เก็บการอ้างอิงไปยังรายการที่ไม่ควรแก้ไขหรือไม่สามารถแบ่งปันการอ้างอิงนั้นโดยตรงกับรหัสภายนอกได้อย่างปลอดภัยหรือไม่ มันสามารถสันนิษฐานได้อย่างปลอดภัยทุกแง่มุมของรายการจะไม่เปลี่ยนแปลง
supercat

14

น่าเสียดายที่อาร์เรย์ไม่ ' classเพียงพอ' พวกเขาไม่ได้ใช้Iterableอินเทอร์เฟซ

ในขณะที่อาร์เรย์เป็นวัตถุที่ใช้ Clonable และ Serializable ฉันเชื่อว่าอาร์เรย์ไม่ใช่วัตถุในความรู้สึกปกติและไม่ได้ใช้อินเทอร์เฟซ

เหตุผลที่คุณสามารถใช้พวกมันในแต่ละลูปได้ก็เพราะว่าซันเพิ่มน้ำตาล syntatic สำหรับอาร์เรย์ (เป็นกรณีพิเศษ)

ตั้งแต่อาร์เรย์เริ่มต้นในฐานะ 'เกือบวัตถุ' กับ Java 1 มันคงเป็นการเปลี่ยนแปลงที่รุนแรงเกินกว่าที่จะทำให้มันเป็นวัตถุจริงใน Java


14
ยังมีน้ำตาลสำหรับแต่ละวงดังนั้นทำไมไม่มีน้ำตาลสำหรับ Iterable?
Michael Myers

8
@mmyers: น้ำตาลที่ใช้ในการผลิตน้ำตาลแต่ละครั้งนั้น มันง่ายกว่าการทำVMน้ำตาล ซึ่งต้องบอกอาร์เรย์ .NET อย่างมีนัยสำคัญที่ดีขึ้นในพื้นที่นี้ ...
จอนสกีต

12
อาร์เรย์สามารถใช้อินเตอร์เฟสได้ พวกเขาใช้CloneableและSerializableอินเทอร์เฟซ
notnoop

34
อาร์เรย์ java เป็นวัตถุในความรู้สึกทั้งหมด โปรดลบข้อมูลที่ผิดเล็กน้อยออก พวกเขาไม่ใช้ Iterable
ykaganovich

5
อาร์เรย์คือวัตถุ รองรับประโยชน์ : วิธีการ P เช่นรอ (), รอ (n), รอ (n, m), แจ้ง (), informAll (), สรุป (), การใช้งานแบบไม่มีจุดหมายของ toString () วิธีการที่มีประโยชน์เพียงอย่างเดียวคือ getClass () .
Peter Lawrey

1

คอมไพเลอร์แปลความจริงfor eachบนอาเรย์เป็นforลูปธรรมดาด้วยตัวแปรตัวนับ

รวบรวมดังต่อไปนี้

public void doArrayForEach() {
    int[] ints = new int[5];

    for(int i : ints) {
        System.out.println(i);
    }
}

จากนั้นแยกส่วนไฟล์. class

public void doArrayForEach() {
    int[] ints = new int[5];
    int[] var2 = ints;
    int var3 = ints.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        int i = var2[var4];
        System.out.println(i);
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.