วิธีที่ง่ายที่สุดในการแปลงคอลเลคชันเป็นอาร์เรย์?


126

สมมติว่าเรามีไฟล์Collection<Foo>. วิธีที่ดีที่สุด (สั้นที่สุดใน LoC ในบริบทปัจจุบัน) ในการแปลงเป็นFoo[]อย่างไร ใด ๆที่รู้จักกันดีในห้องสมุดได้รับอนุญาต

UPD: (กรณีหนึ่งที่เพิ่มเติมในส่วนนี้; ความคิดเห็นลาถ้าคุณคิดว่ามันคุ้มค่าที่จะสร้างหัวข้ออื่นมัน): สิ่งที่เกี่ยวกับการเปลี่ยนCollection<Foo>ไปBar[]ที่Barมีคอนสตรัค 1 พารามิเตอร์ชนิดFooเช่นpublic Bar(Foo foo){ ... }?


สร้างคำตอบนี้ด้วย API ทางเลือกที่แนะนำใน JDK-11 เพื่อดำเนินการเดียวกันโดยมีประสิทธิภาพใกล้เคียงกันและมีคำอธิบายควบคู่ไปด้วย บวกกับไวยากรณ์ตรงกับStream.toArrayAPI ที่มีอยู่จาก JDK
Naman

คำตอบ:


257

xคอลเลกชันอยู่ที่ไหน:

Foo[] foos = x.toArray(new Foo[x.size()]);

35
ง่ายกว่า (ง่ายกว่า) แต่ไม่ดีที่สุด (memory): x.toArray(new Foo[0]) --- documentation : no time to read ...
user85421

6
@Carlos จริงๆแล้วมันใช้หน่วยความจำดีกว่า(new Foo[0]). ตามเอกสาร Collection.toArray `@param คืออาร์เรย์ที่จะจัดเก็บองค์ประกอบของคอลเล็กชันนี้หากมีขนาดใหญ่พอ" ซึ่งหมายความว่าจะจัดเก็บไว้ในอาร์เรย์ใหม่นั้นโดยตรง หากคุณให้อาร์เรย์ขนาด 0 มันจะสร้างอาร์เรย์ใหม่ซึ่งหมายความว่าคุณมีอาร์เรย์ขนาดเล็กและอาร์เรย์ขนาดใหญ่ที่สร้างขึ้นเมื่อไม่จำเป็น
corsiKa

4
@glowcoder - แต่สามารถย่อให้เล็กสุดได้หากคุณกำหนดอาร์เรย์ว่างหนึ่งครั้งเป็นค่าคงที่คงที่ เป็นเพียงคำแนะนำในtoArrayการสร้างอาร์เรย์เป้าหมายของประเภทที่ถูกต้อง และในกรณีนี้โดยสุจริตฉันจะไม่สนใจอาร์เรย์ว่างเปล่าพิเศษหนึ่งชุดในแอปพลิเคชันของฉัน ต่ำกว่าระดับเสียง
Andreas Dolk

2
@glowcoder - นั่นคือเหตุผลที่ฉัน ( พยายาม ) เขียนว่าการใช้new Foo[0]ง่ายกว่า "แต่ไม่ดีที่สุด (หน่วยความจำ)" ... นั่นคือฉันหมายความว่าโซลูชันของฉันง่ายกว่า แต่ไม่ดีที่สุด (นั่นคือเหตุผลที่ฉันใช้:)
user85421

21
โปรดทราบว่ารหัสนี้ไม่ปลอดภัยสำหรับเธรดแม้ว่าคุณจะใช้คอลเล็กชันที่ซิงโครไนซ์ก็ตาม มันจะทำงานเองในสถานการณ์ส่วนใหญ่ แต่ถ้ารายการถูกลบระหว่างการเรียกไปที่ x.size () และ x.toArray () อาร์เรย์ผลลัพธ์จะมีองค์ประกอบว่างเพิ่มเติมในตอนท้าย new Foo[0]ตัวแปรที่เสนอโดยคาร์ลอไม่ประสบปัญหานี้
Jules

36

ทางเลือกอื่นสำหรับคำถามที่อัปเดตโดยใช้ Java 8:

Bar[] result = foos.stream()
    .map(x -> new Bar(x))
    .toArray(size -> new Bar[size]);

35
สามารถย่อได้ถึง Bar[] result = foos.stream().map(Bar::new).toArray(Bar[]::new);
lpandzic

ไวยากรณ์ที่ชาญฉลาดฉันต้องการคำตอบที่ยอมรับมาก ฉันหวังว่าทุกคนสามารถเปลี่ยนมาใช้ Python ได้
Eric Chen

@EricChen เปลี่ยนเป็น Python - ภาษาที่ตีความและพิมพ์แบบไดนามิกจาก Java - 'ภาษาที่รวบรวมและพิมพ์แบบคงที่' เนื่องจากไวยากรณ์การเข้ารหัสและบรรทัดจำนวนแตกต่างกัน? ไม่ใช่ความคิดที่ดี
RafiAlhamd

10

หากคุณใช้มากกว่าหนึ่งครั้งหรือวนซ้ำคุณสามารถกำหนดค่าคงที่ได้

public static final Foo[] FOO = new Foo[]{};

และทำการแปลงตามที่ต้องการ

Foo[] foos = fooCollection.toArray(FOO);

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


นี่คือข้อเสนอของฉันสำหรับการอัปเดต:

Collection<Foo> foos = new ArrayList<Foo>();
Collection<Bar> temp = new ArrayList<Bar>();
for (Foo foo:foos) 
    temp.add(new Bar(foo));
Bar[] bars = temp.toArray(new Bar[]{});

1
อัพconstไม่ใช่ Java! public static final Foo[] FOO = {}
user85421

1
ทำไมต้องเป็นสาธารณะ
Zoltán

@ Zoltán - ทำไมล่ะ? ไม่เปลี่ยนรูปจึงไม่นำเสนอปัญหาด้านความปลอดภัย มันเกะกะเล็กน้อย แต่อาจมีประโยชน์ในโค้ดอื่น ๆ ดังนั้นโดยทั่วไปแล้วไม่เป็นอันตราย บางทีอาจจะสร้างคลาสกลางด้วยค่าคงที่ทั้งหมดแล้วใช้import staticเพื่อหาค่าเหล่านั้น
Jules

@ กำหนดให้การอ้างอิงถึงอาร์เรย์ไม่เปลี่ยนรูป แต่เนื้อหาของมันไม่เปลี่ยนรูป ทุกคนที่อยู่นอกคลาสสามารถแทนที่องค์ประกอบของอาร์เรย์ได้อย่างอิสระ และในทางกลับกันตามหลักทั่วไปฉันเชื่อว่าทุกช่องควรเป็นแบบส่วนตัวจนกว่าจะจำเป็นต้องใช้นอกชั้นเรียน
Zoltán

2
ด้วยความยาวเป็นศูนย์จึงไม่มีเนื้อหาที่สามารถเปลี่ยนแปลงได้
Jules

5

ด้วย JDK / 11 วิธีอื่นในการแปลง a Collection<Foo>เป็นFoo[]สามารถใช้Collection.toArray(IntFunction<T[]> generator)เป็น:

Foo[] foos = fooCollection.toArray(new Foo[0]); // before JDK 11
Foo[] updatedFoos = fooCollection.toArray(Foo[]::new); // after JDK 11

ตามที่@Stuartอธิบายไว้ในรายชื่ออีเมล (เน้นของฉัน) ประสิทธิภาพของสิ่งนี้ควรจะเหมือนกับของที่มีอยู่Collection.toArray(new T[0])-

ผลลัพธ์คือการใช้งานที่ใช้Arrays.copyOf() นั้นเร็วที่สุดอาจเป็นเพราะมันเป็นสิ่งที่อยู่ภายในมันเป็นที่แท้จริง

สามารถหลีกเลี่ยงการเติมอาร์เรย์ที่จัดสรรใหม่เป็นศูนย์ได้เนื่องจากรู้ว่าเนื้อหาอาร์เรย์ทั้งหมดจะถูกเขียนทับ สิ่งนี้เป็นจริงไม่ว่า API สาธารณะจะเป็นอย่างไร

การใช้งาน API ภายใน JDK อ่าน:

default <T> T[] toArray(IntFunction<T[]> generator) {
    return toArray(generator.apply(0));
}

เริ่มต้นใช้งานเรียกร้องที่จะได้รับอาเรย์ความยาวเป็นศูนย์แล้วก็โทรgenerator.apply(0) toArray(T[])นี้จะต้องผ่านเส้นทางได้อย่างรวดเร็วดังนั้นมันเป็นหลักความเร็วเดียวกับArrays.copyOf() toArray(new T[0])


หมายเหตุ : - เพียงแค่ให้คำแนะนำในการใช้ API ควบคู่ไปกับความเข้ากันไม่ได้ย้อนหลังเมื่อใช้สำหรับโค้ดที่มีnullค่าเช่นtoArray(null)เนื่องจากการเรียกเหล่านี้จะคลุมเครือเนื่องจากมีอยู่toArray(T[] a)และจะไม่สามารถรวบรวมได้



3

สำหรับต้นฉบับดูคำตอบdoublep :

Foo[] a = x.toArray(new Foo[x.size()]);

สำหรับการอัปเดต:

int i = 0;
Bar[] bars = new Bar[fooCollection.size()];
for( Foo foo : fooCollection ) { // where fooCollection is Collection<Foo>
    bars[i++] = new Bar(foo);
}    

3

นี่คือทางออกสุดท้ายสำหรับกรณีนี้ในส่วนการอัปเดต (ด้วยความช่วยเหลือของ Google Collections):

Collections2.transform (fooCollection, new Function<Foo, Bar>() {
    public Bar apply (Foo foo) {
        return new Bar (foo);
    }
}).toArray (new Bar[fooCollection.size()]);

แต่แนวทางสำคัญที่นี่ถูกกล่าวถึงในคำตอบของ doublep (ฉันลืมtoArrayวิธีการ)


1
ดูความคิดเห็นของฉันเกี่ยวกับคำตอบของ doublep เกี่ยวกับความปลอดภัยของเธรดซึ่งใช้กับโซลูชันนี้ด้วย new Bar[0]หลีกเลี่ยงปัญหา
Jules

-1

ตัวอย่างเช่นคุณมีคอลเลกชัน ArrayList ที่มีองค์ประกอบคลาสนักเรียน:

List stuList = new ArrayList();
Student s1 = new Student("Raju");
Student s2 = new Student("Harish");
stuList.add(s1);
stuList.add(s2);
//now you can convert this collection stuList to Array like this
Object[] stuArr = stuList.toArray();           // <----- toArray() function will convert collection to array
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.