แปลง Java Array เป็น Iterable


150

ฉันมีอาเรย์ของสิ่งดั้งเดิมเช่นสำหรับ int, int [] foo มันอาจจะเป็นขนาดเล็กหรือไม่

int foo[] = {1,2,3,4,5,6,7,8,9,0};

วิธีที่ดีที่สุดในการสร้างIterable<Integer>จากมันคืออะไร?

Iterable<Integer> fooBar = convert(foo);

หมายเหตุ:

โปรดอย่าตอบโดยใช้ลูป (เว้นแต่คุณจะให้คำอธิบายที่ดีเกี่ยวกับวิธีที่คอมไพเลอร์ทำบางสิ่งที่ฉลาดเกี่ยวกับพวกเขา)

ยังทราบด้วยว่า

int a[] = {1,2,3};
List<Integer> l = Arrays.asList(a);

จะไม่ได้รวบรวม

Type mismatch: cannot convert from List<int[]> to List<Integer>

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

นอกจากนี้หากคุณใช้ห้องสมุดบางอย่าง (เช่น Guava) โปรดอธิบายว่าทำไมสิ่งนี้ถึงดีที่สุด (เนื่องจากมาจาก Google ไม่ใช่คำตอบที่สมบูรณ์: P)

สุดท้ายเนื่องจากดูเหมือนว่าจะมีการบ้านเกี่ยวกับเรื่องนั้นหลีกเลี่ยงการโพสต์รหัสการบ้าน


เป็นไปได้ที่ซ้ำกันของIterator สำหรับอาร์เรย์
NPE

เพิ่มไปยัง LinkedList จากนั้นเพียงแค่ส่งคืนตัววนซ้ำของชุดนั้น

คำตอบ:


117
Integer foo[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

List<Integer> list = Arrays.asList(foo);
// or
Iterable<Integer> iterable = Arrays.asList(foo);

แม้ว่าคุณจะต้องใช้Integerอาเรย์ (ไม่ใช่intอาเรย์) เพื่อให้มันใช้งานได้

สำหรับดั้งเดิมคุณสามารถใช้ฝรั่ง:

Iterable<Integer> fooBar = Ints.asList(foo);
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>15.0</version>
    <type>jar</type>
</dependency>

สำหรับ Java8: (จากคำตอบของ Jin Kwon)

final int[] arr = {1, 2, 3};
final PrimitiveIterator.OfInt i1 = Arrays.stream(arr).iterator();
final PrimitiveIterator.OfInt i2 = IntStream.of(arr).iterator();
final Iterator<Integer> i3 = IntStream.of(arr).boxed().iterator();

10
หมายเหตุสองข้อ: 1) เขามีintไม่ใช่Integer2) Listอยู่แล้วIterableดังนั้นบรรทัดที่สามจึงไม่มีจุดหมาย
maksimov

1
เขาต้องการพูดซ้ำได้ว่าทำไมถึงมีบรรทัดที่สาม
fmucar

5
2 และสาย 3 เป็นตัวเลือกที่ผมจะบอกว่า :)
fmucar

1
นี่ไม่ใช่ส่วนหนึ่งของการบ้านฉันแค่พยายามหลีกเลี่ยงการทำซ้ำรหัสสำหรับฟังก์ชั่นการแก้ไขข้อบกพร่องในการประมวลผลเนื้อหาของอาเรย์หรือรายการ ... ในขณะที่มองไปรอบ ๆ ฉันก็เจอ Arrays.asList (.. ); Eclipse ดูเหมือนจะคิดว่ามันจะไม่ทำสิ่งที่ฉันต้องการ (เช่นมัน infers ผลลัพธ์ของ Arrays.asList (foo) เป็นรายการ <int []> ไม่ใช่รายการ <Integer> ... ) ฉันพบว่าสิ่งนี้น่าสนใจพอสำหรับ คำถาม ... (- แสดงความคิดเห็นในส่วนสาเหตุของการ จำกัด )
ntg

1
โดยทั่วไปใคร ๆ ก็นึกถึงวิธีที่จะทำมากมาย แต่ฉันก็สงสัยว่าอะไรดีที่สุด (ตัวอย่างเช่นลูปที่จะทำให้ช้าลงเมื่อเทียบกับ ... {ดีปัญหาคือฉันไม่สามารถคิดอะไร!: )}) ตรวจสอบstackoverflow.com/questions/1160081/ อีกด้วยเพื่อดูว่าทำไมคำถามของฉันถึงไม่ใช่ว่าทำไม แต่อย่างไรและคอนเทนเนอร์ประเภทใดจะดีที่สุด (ทำไม ArrayList จริงๆแล้วฉันนึกภาพ AbstractList บ้าง) เสื้อคลุมที่ใช้ Generics .. , อาจขึ้นอยู่กับขนาด ... )
ntg

44

แค่ 2 เซ็นต์ของฉัน:

final int a[] = {1,2,3};

java.lang.Iterable<Integer> aIterable=new Iterable<Integer>() {

    public Iterator<Integer> iterator() {
       return new Iterator<Integer>() {
            private int pos=0;

            public boolean hasNext() {
               return a.length>pos;
            }

            public Integer next() {
               return a[pos++];
            }

            public void remove() {
                throw new UnsupportedOperationException("Cannot remove an element of an array.");
            }
        };
    }
};

9
remove () ไม่จำเป็นใน java 8 เพราะเป็นวิธีการเริ่มต้นซึ่งจะพ่น UnsupportedOperationException เฉพาะในกรณีที่คุณต้องการส่งข้อความอธิบายที่ดีกว่า
อเล็กซ์

+1 ฉันทำบางสิ่งบางอย่างที่คล้ายกันในการสร้างจากIterator<Character> StringการดำเนินการของคุณเองIteratorดูเหมือนว่าวิธีเดียวที่จะหลีกเลี่ยงไม่จำเป็นต้องทำซ้ำผ่านทั้งหมดของค่าการแปลงจากชนิดของวัตถุชนิดดั้งเดิม (ผ่านฝรั่งของInts.asList()ตัวอย่าง), เพียงเพื่อให้สามารถที่จะได้รับIteratorจากListที่ถูกสร้างขึ้น
spaaarky21

2
คุณพูดถูกอเล็กซ์ วิธีการเริ่มต้นถูกเพิ่มใน Java 8 ในปี 2013 ฉันเพิ่มโค้ดโบราณนี้ที่นี่
Joerg Ruethschilling

29

ด้วย Java 8 คุณสามารถทำได้

final int[] arr = {1, 2, 3};
final PrimitiveIterator.OfInt i1 = Arrays.stream(arr).iterator();
final PrimitiveIterator.OfInt i2 = IntStream.of(arr).iterator();
final Iterator<Integer> i3 = IntStream.of(arr).boxed().iterator();

20

ฝรั่งยังมีอะแดปเตอร์ที่คุณต้องการเป็นInt.asList () มีความเท่าเทียมกันสำหรับแต่ละประเภทดั้งเดิมในชั้นที่เกี่ยวข้องเช่นBooleansสำหรับbooleanฯลฯ

int foo[] = {1,2,3,4,5,6,7,8,9,0};
Iterable<Integer> fooBar = Ints.asList(foo);
for(Integer i : fooBar) {
    System.out.println(i);
}

ข้อเสนอแนะดังกล่าวข้างต้นไปใช้งานที่Arrays.asListจะไม่ทำงานแม้ว่าพวกเขาจะรวบรวมเพราะคุณจะได้รับมากกว่าIterator<int[]> Iterator<Integer>สิ่งที่เกิดขึ้นคือแทนที่จะสร้างรายการที่ได้รับการสนับสนุนโดยอาร์เรย์ของคุณคุณสร้างรายการ 1 องค์ประกอบของอาร์เรย์ที่มีอาร์เรย์ของคุณ


เพียงแค่ทราบ: ลิงค์ไม่ทำงานอีกต่อไป ลิงค์ Github: github.com/google/guava/blob/master/guava/src/com/google/common/…
Orangle

ขอบคุณ @Passi คงที่ (ดูเหมือนจะไม่พบวิธีที่ Google สนับสนุนเพื่อลิงก์ไปยัง javadoc อีกต่อไปดังนั้นฉันจึงเชื่อมโยงไปยังแหล่งที่คุณให้ไว้)
BeeOnRope

8

ฉันมีปัญหาเดียวกันและแก้ไขได้ดังนี้:

final YourType[] yourArray = ...;
return new Iterable<YourType>() {
  public Iterator<YourType> iterator() {
     return Iterators.forArray(yourArray);   // Iterators is a Google guava utility
  }
}

ตัววนซ้ำตัวเองขี้เกียจUnmodifiableIteratorแต่นั่นคือสิ่งที่ฉันต้องการ


7

ใน Java 8 หรือใหม่กว่าIterableจะส่งคืนอินเตอร์เฟสที่ใช้งานIteratorได้ ดังนั้นคุณสามารถทำได้

int[] array = {1, 2, 3};
Iterable<Integer> iterable = () -> Arrays.stream(array).iterator();
for (int i : iterable)
    System.out.println(i);

->

1
2
3

3

ก่อนอื่นฉันเห็นด้วยArrays.asList(T...)อย่างชัดเจนว่าเป็นทางออกที่ดีที่สุดสำหรับประเภท Wrapper หรืออาร์เรย์ที่มีประเภทข้อมูลที่ไม่ใช่ primtive วิธีนี้เรียกใช้ตัวสร้างของการAbstractListใช้งานสแตติกส่วนตัวอย่างง่ายในArraysคลาสซึ่งโดยทั่วไปจะบันทึกการอ้างอิงอาเรย์ที่กำหนดเป็นฟิลด์และจำลองรายการโดยการแทนที่เมธอดที่จำเป็น

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

1) คุณสามารถสร้างคลาสด้วยวิธีสแตติกสำหรับแต่ละอาร์เรย์ประเภทข้อมูลดั้งเดิม ( boolean, byte, short, int, long, char, float, doubleส่งคืนIterable<WrapperType>วิธีการเหล่านี้จะใช้คลาสที่ไม่ระบุชื่อของIterator(นอกเหนือจากIterable) ซึ่งได้รับอนุญาตให้มีการอ้างอิงของอาร์กิวเมนต์ประกอบด้วยวิธีการ (ตัวอย่างเช่นint[]) เป็นเขตข้อมูลเพื่อที่จะใช้วิธีการ

-> วิธีการนี้มีประสิทธิภาพและช่วยให้คุณประหยัดหน่วยความจำ (ยกเว้นความทรงจำของวิธีการที่สร้างขึ้นใหม่แม้ว่าการใช้Arrays.asList()จะใช้หน่วยความจำในลักษณะเดียวกัน)

2) เนื่องจากอาร์เรย์ไม่มีวิธีการ (ที่จะอ่านด้านข้าง คุณเชื่อมโยง) พวกเขาไม่สามารถให้Iteratorอินสแตนซ์ได้ หากคุณขี้เกียจเกินไปที่จะเขียนคลาสใหม่คุณต้องใช้อินสแตนซ์ของคลาสที่มีอยู่แล้วที่ใช้งานIterableเพราะไม่มีวิธีอื่นนอกเหนือจากการสร้างอินสแตนซ์Iterableหรือประเภทย่อย
วิธีเดียวในการสร้างการรวบรวมตราสารอนุพันธ์ที่มีอยู่Iterableคือการใช้ลูป (ยกเว้นคุณใช้คลาสที่ไม่ระบุชื่อตามที่อธิบายไว้ข้างต้น) หรือคุณสร้างอินสแตนซ์ของสำหรับคอลเลกชันแต่ละรายการที่คุณต้องการวัตถุและประเภทข้อมูลหลักไม่ใช่วัตถุ วัตถุมีขนาดใหญ่กว่าชนิดดั้งเดิมเพื่อให้พวกเขาต้องการข้อมูลเพิ่มเติมซึ่งจะต้องสร้างขึ้นสำหรับแต่ละองค์ประกอบของอาร์เรย์ชนิดดั้งเดิม ซึ่งหมายความว่าหากสองวิธีในสามวิธี (การใช้หรือใช้คอลเล็กชันที่มีอยู่) ต้องการการรวมวัตถุคุณต้องสร้างสำหรับค่าดั้งเดิมแต่ละค่าของคุณIterableการนำคลาสมาใช้ซึ่งคอนสตรัคเตอร์อนุญาตให้มีอาเรย์ประเภท primtive (เพราะObject[]ไม่อนุญาตให้อาร์เรย์ที่มีองค์ประกอบประเภทดั้งเดิม) แต่เท่าที่ฉันรู้ Java API ไม่ได้มีคุณสมบัติในชั้นเรียนเช่นนั้น

เหตุผลของการวนซ้ำสามารถอธิบายได้ง่าย ๆ :
Arrays.asList(T...)int[]อาร์เรย์วัตถุห่อหุ้ม วิธีที่สามจะใช้อาเรย์ตามที่เป็นอยู่และใช้ในคลาสนิรนามเนื่องจากฉันคิดว่ามันดีกว่าเนื่องจากประสิทธิภาพที่รวดเร็ว

นอกจากนี้ยังมีกลยุทธ์ที่สามที่ใช้Objectอาร์กิวเมนต์เป็นวิธีการที่คุณต้องการใช้อาร์เรย์หรือIterableและจะต้องมีการตรวจสอบประเภทเพื่อหาว่าอาร์กิวเมนต์มีประเภทใด แต่ฉันจะไม่แนะนำเลยเพราะคุณจำเป็นต้อง พิจารณาว่า Object ไม่ได้เป็นประเภทที่จำเป็นเสมอไปและคุณต้องแยกรหัสสำหรับบางกรณี

สรุปแล้วมันเป็นความผิดพลาดของระบบ Generic Type ที่เป็นปัญหาของ Java ซึ่งไม่อนุญาตให้ใช้ประเภทดั้งเดิมเป็นประเภททั่วไปซึ่งจะบันทึกรหัสจำนวนมากโดยใช้เพียงArrays.asList(T...). ดังนั้นคุณจำเป็นต้องเขียนโปรแกรมสำหรับอาเรย์แบบดั้งเดิมแต่ละอันคุณต้องการวิธีการ (ซึ่งโดยทั่วไปแล้วจะไม่สร้างความแตกต่างให้กับหน่วยความจำที่ใช้โดยโปรแกรม C ++ ซึ่งจะสร้างขึ้นสำหรับอาร์กิวเมนต์ประเภทที่ใช้แล้วแต่ละวิธี


3

คุณสามารถใช้IterableOfจากCactoos :

Iterable<String> names = new IterableOf<>(
  "Scott Fitzgerald", "Fyodor Dostoyevsky"
);

จากนั้นคุณสามารถเปลี่ยนเป็นรายการโดยใช้ListOf:

List<String> names = new ListOf<>(
  new IterableOf<>(
    "Scott Fitzgerald", "Fyodor Dostoyevsky"
  )
);

หรือเพียงแค่นี้:

List<String> names = new ListOf<>(
  "Scott Fitzgerald", "Fyodor Dostoyevsky"
);

1

ในขณะที่คำตอบที่คล้ายกันได้รับการโพสต์แล้วฉันคิดว่าเหตุผลที่ใช้ PrimitiveIterator ใหม่อ็อฟฟินต์ไม่ชัดเจน ทางออกที่ดีคือการใช้ Java 8 PrimitiveIterator เนื่องจากเป็นผู้เชี่ยวชาญสำหรับประเภท int ดั้งเดิม (และหลีกเลี่ยงการลงโทษมวย / unboxing พิเศษ):

    int[] arr = {1,2,3};
    // If you use Iterator<Integer> here as type then you can't get the actual benefit of being able to use nextInt() later
    PrimitiveIterator.OfInt iterator = Arrays.stream(arr).iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.nextInt());
        // Use nextInt() instead of next() here to avoid extra boxing penalty
    }

Ref: https://doc.bccnsoft.com/docs/jdk8u12-docs/api/java/util/PrimitiveIterator.OfInt.html


-2

ใน java8 IntSteam สตรีมสามารถใส่กล่องเพื่อสตรีมของจำนวนเต็ม

public static Iterable<Integer> toIterable(int[] ints) {
    return IntStream.of(ints).boxed().collect(Collectors.toList());
}

ฉันคิดว่าเรื่องประสิทธิภาพขึ้นอยู่กับขนาดของอาเรย์

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