วิธีง่ายๆในการแปลง Iterable เป็น Collection


424

ในแอปพลิเคชันของฉันฉันใช้ห้องสมุดบุคคลที่สาม (Spring Data สำหรับ MongoDB เป็นที่แน่นอน)

วิธีการของการกลับมาห้องสมุดนี้ขณะที่คาดว่าส่วนที่เหลือของรหัสของฉันIterable<T>Collection<T>

มีวิธีการอรรถประโยชน์ใดที่จะให้ฉันแปลงอย่างรวดเร็วอื่น ๆ ? ฉันต้องการหลีกเลี่ยงการสร้างforeachวนลูปในรหัสของฉันสำหรับสิ่งง่าย ๆ


3
วิธี utiliy ใด ๆ สำหรับการดำเนินการถูกผูกไว้กับ iterate ของคอลเลกชันอยู่แล้วดังนั้นคุณไม่สามารถคาดหวังประสิทธิภาพที่เพิ่มขึ้น แต่ถ้าคุณแค่มองหาน้ำตาลฉันจะไปหา Guava หรือ Apache Collections
Sebastian Ganslandt

" ถูกผูกไว้กับการสะสมซ้ำแล้วซ้ำอีก ", - ไม่เลย ดูคำตอบของฉันสำหรับรายละเอียด
aioobe

3
ใน usecase เฉพาะของคุณคุณสามารถขยาย CrudRepository ด้วยอินเทอร์เฟซของคุณเองด้วยเมธอดที่ส่งกลับคอลเล็กชัน <T> / List <T> / Set <T> (ตามต้องการ) แทน Iterable <T>
Kevin Van Dyck

คำตอบ:


387

ด้วยGuavaคุณสามารถใช้Lists.newArrayList (Iterable)หรือSets.newHashSet (Iterable)ท่ามกลางวิธีการอื่นที่คล้ายคลึงกัน แน่นอนนี้จะคัดลอกองค์ประกอบทั้งหมดในหน่วยความจำ ถ้าเป็นที่ยอมรับไม่ได้ผมคิดว่ารหัสของคุณที่ทำงานร่วมกับเหล่านี้ควรจะใช้เวลามากกว่าIterable Collectionฝรั่งยังให้วิธีการที่สะดวกในการทำสิ่งต่าง ๆ ที่คุณสามารถทำได้Collectionโดยใช้Iterable(เช่นIterables.isEmpty(Iterable)หรือIterables.contains(Iterable, Object)) แต่ความหมายของประสิทธิภาพนั้นชัดเจนกว่า


1
มันวนซ้ำผ่านองค์ประกอบทั้งหมดโดยตรงหรือไม่ คือLists.newArrayList(Iterable).clear()การดำเนินการเชิงเส้นหรือเวลาคงที่คืออะไร?
aioobe

2
@aioobe: มันสร้างสำเนาของ iterable ไม่ได้ระบุว่าต้องการมุมมองและเนื่องจากวิธีการส่วนใหญ่Collectionไม่สามารถนำไปใช้สำหรับมุมมองIterableหรือจะไม่มีประสิทธิภาพได้
ColinD

@ColinD จะทำอย่างไรถ้าฉันต้องการมุมมอง ที่จริงแล้วสิ่งที่ฉันต้องการคือมุมมองคอลเล็กชันที่เป็นผลลัพธ์ของการผนวกคอลเลกชันแหล่งที่มากับองค์ประกอบอื่น ฉันสามารถใช้Iterables.concat()แต่นั่นให้Iterableไม่ใช่Collection:(
Hendy Irawan

1
นี่คือคำถามของฉัน: stackoverflow.com/questions/4896662/... Iterables.concat()แต่น่าเสียดายที่คำตอบง่ายๆว่าไม่สามารถแก้ปัญหาได้โดยใช้ คำตอบที่นานกว่านั้นให้Collection... ฉันสงสัยว่าทำไมสิ่งนี้ถึงไม่รองรับกันมากกว่า
Hendy Irawan

365

ใน JDK 8+ โดยไม่ใช้ libs เพิ่มเติม:

Iterator<T> source = ...;
List<T> target = new ArrayList<>();
source.forEachRemaining(target::add);

แก้ไข: Iteratorหนึ่งข้างต้นสำหรับ หากคุณกำลังเผชิญกับIterable,

iterable.forEach(target::add);

86
หรือiterable.forEach(target::add);
ฟาโลพอด

92

คุณสามารถเขียนวิธีการอรรถประโยชน์ของคุณเองสำหรับสิ่งนี้เช่นกัน:

public static <E> Collection<E> makeCollection(Iterable<E> iter) {
    Collection<E> list = new ArrayList<E>();
    for (E item : iter) {
        list.add(item);
    }
    return list;
}

33
+1 ถ้าไปจากIterableการCollectionเป็นกังวลเท่านั้นฉันชอบวิธีนี้มากกว่าการนำเข้าของบุคคลที่ 3 คอลเลกชัน-ห้องสมุดขนาดใหญ่
aioobe

2
รหัสฟังก์ชั่น 4 บรรทัดเป็นที่นิยมมากกว่ารหัสไลบรารีที่รวบรวม 2 MB ซึ่งไม่ได้ใช้งาน 99% มีค่าใช้จ่ายอื่น: ภาวะแทรกซ้อนของการออกใบอนุญาต สิทธิ์การใช้งาน Apache 2.0 นั้นมีความยืดหยุ่น แต่ไม่จำเป็นต้องมีเอกสารที่น่าเบื่อ จะเป็นการดีที่เราจะได้เห็นบางส่วนของรูปแบบเหล่านี้ร่วมกันแบบบูรณาการโดยตรงใน Java Runtime ห้องสมุด
Jonathan Neufeld

2
อีกจุดหนึ่งเนื่องจากคุณกำลังใช้ ArrayList แต่ทำไมไม่ไปกับประเภทรายการ covariant แทน สิ่งนี้จะช่วยให้คุณสามารถทำตามสัญญาได้มากขึ้นโดยไม่ต้องทำการดาวน์โลดหรือแนะนำใหม่อีกครั้งและ Java ก็ยังไม่รองรับขอบเขตประเภทที่ต่ำกว่า
Jonathan Neufeld

@JonathanNeufeld หรือทำไมไม่เพียงแค่ไปข้างหน้าและส่งกลับ ArrayList <T>?
Juan

5
@ Juan เพราะนั่นไม่ได้เป็นอย่างSOLID ArrayList จะเปิดเผยรายละเอียดของการติดตั้งที่ไม่จำเป็น (YAGNI) ซึ่งเป็นการละเมิดความรับผิดชอบและหลักการผกผันของการพึ่งพาอาศัยเดี่ยว ฉันจะปล่อยไว้ที่ List เพราะมันจะเปิดเผยมากกว่า Collection ในขณะที่เหลือ SOLID อย่างสมบูรณ์ หากคุณกังวลเกี่ยวกับผลกระทบด้านประสิทธิภาพของ JVM ของ opcode INVOKEINTERFACE ผ่าน INVOKEVIRTUAL มาตรฐานที่มีมากมายจะแสดงให้เห็นว่าไม่คุ้มที่คุณจะนอนไม่หลับ
Jonathan Neufeld

80

โซลูชันที่กระชับกับ Java 8 โดยใช้java.util.stream:

public static <T> List<T> toList(final Iterable<T> iterable) {
    return StreamSupport.stream(iterable.spliterator(), false)
                        .collect(Collectors.toList());
}

1
วิธีนี้ช้าเกินไปเมื่อเทียบกับIteratorUtilsจากcommons-collections
Alex Burdusel

3
ช้าเท่าไหร่ IteratorUtils.toList()ใช้ตัววนซ้ำในรูปแบบ Java 5 ก่อนเพื่อเพิ่มองค์ประกอบทีละรายการในรายการที่สร้างขึ้นใหม่ ง่ายและเร็วที่สุด แต่เพิ่ม 734 kB ไปยังไบนารีของคุณและคุณสามารถทำได้ด้วยตัวคุณเองหากคุณพบว่าวิธีนี้ดีที่สุด
xehpuk

8
ฉันได้ทำเกณฑ์มาตรฐานดั้งเดิมซึ่งสรุปว่าบางครั้งครั้งแรกเร็วกว่าบางครั้งครั้งที่สองเร็วกว่า แสดงมาตรฐานของคุณ
xehpuk

prob นี้อาจเป็นคำตอบที่ยอมรับใหม่ - มันดีที่จะหลีกเลี่ยง libs ที่มากเกินไป (เช่น Guava)
java-addict301

48

IteratorUtilsจากcommons-collectionsอาจช่วย (แม้ว่าพวกเขาไม่สนับสนุน generics ในรุ่นเสถียรล่าสุด 3.2.1):

@SuppressWarnings("unchecked")
Collection<Type> list = IteratorUtils.toList(iterable.iterator());

เวอร์ชั่น 4.0 (ซึ่งอยู่ใน SNAPSHOT ในขณะนี้) รองรับชื่อสามัญและคุณสามารถกำจัด @SuppressWarningsสนับสนุนข้อมูลทั่วไปและคุณสามารถกำจัด

ปรับปรุง: ตรวจสอบIterableAsListจากCactoos


5
แต่นั่นต้องใช้ Iterator ไม่ใช่ Iterable
23915 hithwen

5
@hithwen ฉันไม่เข้าใจ - Iterable ให้ Iterator (รายละเอียดในคำตอบ) - ปัญหาคืออะไร?
ทอม

ไม่รู้ว่าฉันคิดอะไรอยู่ ^^ U
hithwen

2
ตั้งแต่ 4.1 นอกจากนี้ยังมีIterableUtils.toList(Iterable)ซึ่งเป็นวิธีการอำนวยความสะดวกและการใช้งานIteratorUtilsภายใต้ประทุน แต่ก็มีความปลอดภัยเช่นIteratorUtils.toListกัน
Yoory N.

21

จากCollectionUtils :

List<T> targetCollection = new ArrayList<T>();
CollectionUtils.addAll(targetCollection, iterable.iterator())

นี่คือแหล่งที่มาเต็มรูปแบบของวิธีการยูทิลิตี้นี้:

public static <T> void addAll(Collection<T> collection, Iterator<T> iterator) {
    while (iterator.hasNext()) {
        collection.add(iterator.next());
    }
}

มันวนซ้ำผ่านองค์ประกอบทั้งหมดโดยตรงหรือไม่ คือLists.newArrayList(someIterable).clear()การดำเนินการเชิงเส้นหรือเวลาคงที่คืออะไร?
aioobe

ฉันเพิ่มซอร์สโค้ดของaddAllเป็นชื่อที่แสดงถึงมันจะคัดลอกค่าตัววนซ้ำหลังจากนั้นอีกหนึ่ง มันสร้างสำเนามากกว่ามุมมอง
Tomasz Nurkiewicz

ช่างน่าเสียดายที่ไม่มีวิธีการในCollectionUtilsการข้ามการสร้างคอลเลกชันในบรรทัดพิเศษ
Karl Richter

Broken Link ☝️☝️
Hola Soy Edu Feliz Navidad

14

อย่าลืมว่าคอลเลคชั่นทั้งหมดมีขอบเขต จำกัด ในขณะที่ Iterable ไม่มีสัญญาใด ๆ ทั้งสิ้น หากสิ่งที่ Iterable คุณสามารถรับ Iterator และนั่นคือ

for (piece : sthIterable){
..........
}

จะถูกขยายเป็น:

Iterator it = sthIterable.iterator();
while (it.hasNext()){
    piece = it.next();
..........
}

it.hasNext () ไม่จำเป็นต้องส่งคืนค่าเท็จ ดังนั้นในกรณีทั่วไปคุณไม่สามารถคาดหวังได้ว่าจะสามารถแปลง Iterable ทุกชุดเป็นคอลเล็กชันได้ ตัวอย่างเช่นคุณสามารถวนซ้ำจำนวนธรรมชาติทั้งหมดบวกซ้ำสิ่งที่มีรอบในนั้นที่ให้ผลลัพธ์เดียวกันซ้ำแล้วซ้ำอีก ฯลฯ

มิฉะนั้น: คำตอบของ Atrey ค่อนข้างดี


1
มีใครเคยเจอ Iterable ที่วนซ้ำสิ่งที่ไม่มีที่สิ้นสุด (เช่นตัวเลขธรรมชาติที่ให้ไว้ในคำตอบ) ในทางปฏิบัติ / รหัสจริง ฉันคิดว่าเช่น Iterable จะทำให้เกิดความเจ็บปวดและความทุกข์ในหลายสถานที่ ... :)
เดวิด

2
@ David ถึงแม้ว่าฉันจะไม่สามารถชี้ไปที่ Iterator ที่ไม่สิ้นสุดในรหัสการผลิตใด ๆ ของฉันได้ แต่ฉันสามารถนึกถึงกรณีที่อาจเกิดขึ้นได้ วิดีโอเกมอาจมีทักษะที่สร้างไอเท็มในรูปแบบที่เป็นวงกลมซึ่งคำตอบข้างต้นแสดงให้เห็น ในขณะที่ฉันยังไม่พบตัววนซ้ำที่ไม่มีที่สิ้นสุดฉันได้พบตัววนซ้ำที่แน่นอนซึ่งหน่วยความจำเป็นปัญหาที่แท้จริง ฉันมีตัววนซ้ำบนไฟล์ในดิสก์ ถ้าฉันมีดิสก์ 1TB เต็มรูปแบบและ RAM 4GB ฉันสามารถเรียกใช้หน่วยความจำหมดให้เปลี่ยนเป็นตัวรวบรวมได้อย่างง่ายดาย
radicaledward101

14

ฉันใช้FluentIterable.from(myIterable).toList()มาก


9
ควรสังเกตว่ามันมาจากฝรั่งมาก
Vadzim

หรือจาก org.apache.commons.collections4 แล้วก็ FluentIterable.of (myIterable) .toList ()
du-มัน

9

นี่ไม่ใช่คำตอบสำหรับคำถามของคุณ แต่ฉันเชื่อว่านี่เป็นวิธีแก้ปัญหาของคุณ อินเทอร์เฟซorg.springframework.data.repository.CrudRepositoryมีวิธีการที่ส่งคืนjava.lang.Iterableแต่คุณไม่ควรใช้อินเทอร์เฟซนี้ org.springframework.data.mongodb.repository.MongoRepositoryแทนที่จะใช้อินเตอร์เฟซย่อยในกรณีของคุณ java.util.Listอินเตอร์เฟซนี้มีวิธีการที่ผลตอบแทนจากวัตถุชนิด


2
ฉันจะส่งเสริมการใช้ CrudRepository ทั่วไปเพื่อหลีกเลี่ยงการผูกรหัสของคุณกับการใช้งานที่เป็นรูปธรรม
stanlick

7

ฉันใช้ยูทิลิตี้ที่กำหนดเองของฉันเพื่อส่งคอลเลกชันที่มีอยู่ถ้ามี

หลัก:

public static <T> Collection<T> toCollection(Iterable<T> iterable) {
    if (iterable instanceof Collection) {
        return (Collection<T>) iterable;
    } else {
        return Lists.newArrayList(iterable);
    }
}

ตามหลักการข้างต้นจะใช้ ImmutableList แต่ ImmutableCollection ไม่อนุญาตให้มี null ซึ่งอาจให้ผลลัพธ์ที่ไม่พึงประสงค์

แบบทดสอบ:

@Test
public void testToCollectionAlreadyCollection() {
    ArrayList<String> list = Lists.newArrayList(FIRST, MIDDLE, LAST);
    assertSame("no need to change, just cast", list, toCollection(list));
}

@Test
public void testIterableToCollection() {
    final ArrayList<String> expected = Lists.newArrayList(FIRST, null, MIDDLE, LAST);

    Collection<String> collection = toCollection(new Iterable<String>() {
        @Override
        public Iterator<String> iterator() {
            return expected.iterator();
        }
    });
    assertNotSame("a new list must have been created", expected, collection);
    assertTrue(expected + " != " + collection, CollectionUtils.isEqualCollection(expected, collection));
}

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


1
คำตอบอายุปีของคุณเป็นพื้นฐานของคำถามใหม่stackoverflow.com/questions/32570534/..ดึงดูดมุมมองและความคิดเห็นมากมาย
Paul Boddington

6

ทันทีที่คุณโทรcontains, containsAll, equals, hashCode, remove, retainAll, sizeหรือtoArrayคุณต้องการมีการสำรวจองค์ประกอบต่อไป

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

ฉันไม่รู้จักคลาสดังกล่าวในห้องสมุดใด ๆ แต่ควรเป็นแบบฝึกหัดง่ายๆในการเขียน



6

ใน Java 8 คุณสามารถทำสิ่งนี้เพื่อเพิ่มองค์ประกอบทั้งหมดจากIterableถึงCollectionและส่งคืน:

public static <T> Collection<T> iterableToCollection(Iterable<T> iterable) {
  Collection<T> collection = new ArrayList<>();
  iterable.forEach(collection::add);
  return collection;
}

แรงบันดาลใจจาก @Afreys คำตอบ


5

เนื่องจาก RxJava เป็นค้อนและชนิดนี้ดูเหมือนเล็บคุณจึงสามารถทำได้

Observable.from(iterable).toList().toBlocking().single();

23
มีวิธีการ jquery ที่เกี่ยวข้องอาจจะมีบางอย่าง?
Dmitry Minkovsky

3
มันจะล่มถ้ามีรายการที่ไม่มีค่าใน RxJava ไม่ใช่เหรอ
MBH

ฉันเชื่อว่า RxJava2 ไม่อนุญาตรายการที่เป็นโมฆะควรใช้ได้ใน RxJava
DariusL

4

นี่คือ SSCCE สำหรับวิธีที่ยอดเยี่ยมในการทำเช่นนี้ใน Java 8

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class IterableToCollection {
    public interface CollectionFactory <T, U extends Collection<T>> {
        U createCollection();
    }

    public static <T, U extends Collection<T>> U collect(Iterable<T> iterable, CollectionFactory<T, U> factory) {
        U collection = factory.createCollection();
        iterable.forEach(collection::add);
        return collection;
    }

    public static void main(String[] args) {
        Iterable<Integer> iterable = IntStream.range(0, 5).boxed().collect(Collectors.toList());
        ArrayList<Integer> arrayList = collect(iterable, ArrayList::new);
        HashSet<Integer> hashSet = collect(iterable, HashSet::new);
        LinkedList<Integer> linkedList = collect(iterable, LinkedList::new);
    }
}

2

สองข้อสังเกต

  1. ไม่จำเป็นต้องแปลง Iterable เป็น Collection เพื่อใช้ foreach loop - Iterable อาจใช้ใน loop ดังกล่าวโดยตรงไม่มีความแตกต่างทางไวยากรณ์ดังนั้นฉันจึงไม่เข้าใจว่าทำไมคำถามเดิมจึงถูกถามเลย
  2. วิธีที่แนะนำในการแปลง Iterable เป็น Collection ไม่ปลอดภัย (เช่นเดียวกับ CollectionUtils) - ไม่มีการรับประกันว่าการเรียกที่ตามมาไปยังเมธอดถัดไป () จะส่งคืนอินสแตนซ์ของวัตถุที่แตกต่างกัน นอกจากนี้ความกังวลนี้ไม่ได้เป็นทฤษฎีบริสุทธิ์ เช่นการนำ Iterable ไปใช้เพื่อส่งผ่านค่าไปยังวิธีลดขนาดของ Hadoop Reducer จะส่งกลับค่าอินสแตนซ์ค่าเดียวกันเสมอโดยมีค่าของฟิลด์ต่างกัน ดังนั้นถ้าคุณใช้ makeCollection จากด้านบน (หรือ CollectionUtils.addAll (Iterator)) คุณจะได้คอลเลคชั่นที่มีองค์ประกอบที่เหมือนกันทั้งหมด

2

ฉันเจอสถานการณ์ที่คล้ายกันในขณะที่พยายามดึง a ListของProjectแทนที่จะเป็นค่าเริ่มต้นที่Iterable<T> findAll()ประกาศในCrudRepositoryอินเทอร์เฟซ ดังนั้นในของฉันProjectRepositoryอินเตอร์เฟซ (ซึ่งยื่นออกมาจากCrudRepository) ฉันก็ประกาศfindAll()วิธีการที่จะกลับมาเป็นแทนList<Project>Iterable<Project>

package com.example.projectmanagement.dao;

import com.example.projectmanagement.entities.Project;
import org.springframework.data.repository.CrudRepository;
import java.util.List;

public interface ProjectRepository extends CrudRepository<Project, Long> {

    @Override
    List<Project> findAll();
}

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



1

ฉันไม่เห็นวิธีแก้ปัญหาหนึ่งบรรทัดที่เรียบง่ายโดยไม่มีการอ้างอิงใด ๆ ฉันใช้ง่าย

List<Users> list;
Iterable<IterableUsers> users = getUsers();

// one line solution
list = StreamSupport.stream(users.spliterator(), true).collect(Collectors.toList());

0

คุณสามารถใช้โรงงานEclipse Collections :

Iterable<String> iterable = Arrays.asList("1", "2", "3");

MutableList<String> list = Lists.mutable.withAll(iterable);
MutableSet<String> set = Sets.mutable.withAll(iterable);
MutableSortedSet<String> sortedSet = SortedSets.mutable.withAll(iterable);
MutableBag<String> bag = Bags.mutable.withAll(iterable);
MutableSortedBag<String> sortedBag = SortedBags.mutable.withAll(iterable);

นอกจากนี้คุณยังสามารถแปลงIterablea LazyIterableและใช้วิธีการแปลงหรือ API อื่น ๆ ที่มีอยู่

Iterable<String> iterable = Arrays.asList("1", "2", "3");
LazyIterable<String> lazy = LazyIterate.adapt(iterable);

MutableList<String> list = lazy.toList();
MutableSet<String> set = lazy.toSet();
MutableSortedSet<String> sortedSet = lazy.toSortedSet();
MutableBag<String> bag = lazy.toBag();
MutableSortedBag<String> sortedBag = lazy.toSortedBag();

ทั้งหมดข้างต้นประเภทขยายMutablejava.util.Collection

หมายเหตุ: ฉันเป็นคอมมิชชันสำหรับ Eclipse Collections

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