Iterable को Collection में बदलने का आसान तरीका


424

अपने आवेदन में मैं 3rd पार्टी लाइब्रेरी (सटीक होने के लिए MongoDB के लिए स्प्रिंग डेटा) का उपयोग करता हूं।

इस लाइब्रेरी के तरीके वापस आ गए Iterable<T>, जबकि मेरे बाकी कोड की उम्मीद है Collection<T>

क्या कहीं कोई उपयोगिता विधि है जो मुझे जल्दी से एक को दूसरे में बदलने देगी? मैं foreachइतनी सरल बात के लिए अपने कोड में छोरों का एक गुच्छा बनाने से बचना चाहूंगा ।


3
ऑपरेशन करने के लिए कोई भी उपयुक्त तरीका वैसे भी संग्रह के पुनरावृति के लिए बाध्य है, इसलिए आप किसी भी प्रदर्शन लाभ की उम्मीद नहीं कर सकते। लेकिन अगर आप सिंटैक्टिक शुगर की तलाश में हैं तो मैं अमरूद या शायद अपाचे कलेक्शन के लिए जाऊंगा।
सेबेस्टियन गन्सलैंड

" वैसे भी संग्रह के पुनरावृति के लिए बाध्य है ", - नहीं, यह नहीं है। विवरण के लिए मेरा उत्तर देखें।
aioobe

3
अपने विशिष्ट usecase में, आप अपने तरीके के साथ CrudRepository का विस्तार कर सकते हैं, जो संग्रह के लिए <T> / List <T> / Set <T> (as आवश्यक) के बजाय Iterable <T>
केविन वैन डाइक

जवाबों:


387

अमरूद के साथ आप अन्य समान विधियों में Lists.newArrayList (Iterable) या Sets.newHashSet (Iterable) का उपयोग कर सकते हैं । यह निश्चित रूप से सभी तत्वों को स्मृति में कॉपी करेगा। यदि वह स्वीकार्य नहीं है, तो मुझे लगता है कि आपका कोड जो इन के साथ काम करना चाहता है, Iterableबल्कि लेना चाहिए Collection। अमरूद उन चीजों को करने के लिए सुविधाजनक तरीके प्रदान करने के लिए भी होता है जिन्हें आप Collectionएक Iterable(जैसे Iterables.isEmpty(Iterable)या Iterables.contains(Iterable, Object)) का उपयोग करके कर सकते हैं , लेकिन प्रदर्शन के निहितार्थ अधिक स्पष्ट हैं।


1
क्या यह सीधे सभी तत्वों के माध्यम से पुनरावृति करता है? यानी, Lists.newArrayList(Iterable).clear()एक रैखिक या निरंतर समय ऑपरेशन है?
Aioobe

2
@ वायो: यह पुनरावृत्ति की एक प्रति बनाता है। यह निर्दिष्ट नहीं किया गया था कि एक दृश्य वांछित था, और यह देखते हुए कि अधिकांश तरीकों को Collectionया तो एक दृश्य के लिए लागू नहीं किया जा सकता है Iterableया कुशल नहीं होगा, यह मेरे लिए ऐसा करने का कोई मतलब नहीं है।
कॉलिनड

@ColinD क्या होगा अगर मुझे एक दृश्य चाहिए? वास्तव में, मैं जो चाहता हूं वह एक संग्रह दृश्य है जो स्रोत संग्रह को किसी अन्य तत्व के साथ जोड़ने का परिणाम है। मैं उपयोग कर सकता हूं , Iterables.concat()लेकिन Iterableयह ए Collection: नहीं देता है :(
हेंडी इरावन

1
यह मेरा प्रश्न है: stackoverflow.com/questions/4896662/… । दुर्भाग्य से सरल उत्तर जो समस्या को हल नहीं करता है वह उपयोग करके है Iterables.concat()। बहुत लंबा जवाब देता है Collection... मुझे आश्चर्य है कि यह अधिक सामान्य रूप से समर्थित क्यों नहीं है?
हेंडी इरावन

365

JDK 8+ में, बिना किसी अतिरिक्त शुल्क का उपयोग किए:

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 एमबी से अधिक बेहतर हैं, जिसके लिए 99% अप्रयुक्त हो जाता है। एक और लागत है: लाइसेंस जटिलताओं। Apache 2.0 लाइसेंस लचीला है, लेकिन कुछ थकाऊ जनादेशों के बिना नहीं। आदर्श रूप से हम इनमें से कुछ सामान्य पैटर्न को सीधे जावा रनटाइम लाइब्रेरी में एकीकृत करते हुए देखेंगे ।
जोनाथन नेफेल्ड

2
एक और बात, जब से आप किसी भी तरह से एक ArrayList का उपयोग कर रहे हैं, तो इसके बजाय केवल सहसंयोजक सूची प्रकार के साथ क्यों न जाएं? यह आपको डाउन-कास्टिंग या पुन: प्रस्ताव के बिना अधिक अनुबंधों को पूरा करने की अनुमति देता है और जावा को वैसे भी निचले प्रकार की सीमा के लिए कोई समर्थन नहीं है।
जोनाथन नेफेल्ड

@JonathanNeufeld या क्यों न केवल आगे बढ़ें और एक ArrayList <T> लौटाएं?
जुआन

5
@ जुआन क्योंकि यह बहुत ठोस नहीं है । एक ArrayList कार्यान्वयन विवरणों को उजागर करता है जो कि सबसे अधिक संभावनाहीन (YAGNI) हैं, जो एकल जिम्मेदारी और निर्भरता आमंत्रण सिद्धांतों का उल्लंघन करता है। मैं इसे सूची में छोड़ दूंगा क्योंकि यह पूरी तरह से ठोस होने के दौरान संग्रह की तुलना में थोड़ा अधिक उजागर करता है। यदि आप INVOKEVIRTUAL पर opcode INVOKEINTERFACE के JVM प्रदर्शन प्रभाव के बारे में चिंतित हैं, तो बहुत सारे मानदंड यह बताएंगे कि यह नींद खोने के लायक नहीं है।
जोनाथन नेफल्ड

80

जावा 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
एलेक्स Burdusel

3
कितना धीमा है? IteratorUtils.toList()एक पूर्व जावा 5 फैशन में पुनरावृत्त का उपयोग करता है ताकि तत्वों को एक-एक करके नई बनाई गई सूची में जोड़ा जा सके। सरल और संभवतः सबसे तेज़, लेकिन आपके बाइनरी में 734 kB जोड़ता है और आप इसे अपने दम पर कर सकते हैं यदि आपको यह तरीका सबसे अच्छा लगता है।
xehpuk

8
मैंने एक आदिम बेंचमार्क यह निष्कर्ष निकाला है कि कभी-कभी पहला तेज होता है, कभी-कभी दूसरा तेज होता है। हमें अपना बेंचमार्क दिखाएं।
xehpuk

यह संभावना नया स्वीकृत उत्तर हो सकता है - अतिरिक्त देयताओं (जैसे अमरूद) से बचना अच्छा है।
जावा-आदी ३००१

48

IteratorUtilsसे commons-collectionsमदद मिल सकती है (हालांकि वे नवीनतम स्थिर संस्करण 3.2.1 में जेनरिक का समर्थन नहीं करते हैं):

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

संस्करण 4.0 (जो इस समय SNAPSHOT में है) जेनरिक का समर्थन करता है और आप इससे छुटकारा पा सकते हैं @SuppressWarnings

अपडेट: कैक्टसIterableAsList से जांचें


5
लेकिन इसके लिए एक Iterator की आवश्यकता होती है, एक Iterable की नहीं
hithwen

5
@ हितेन, मुझे नहीं मिला - Iterable एक Iterator प्रदान करता है (जैसा कि उत्तर में विस्तृत है) - क्या समस्या है?
टॉम

पता नहीं मैं क्या सोच रहा था ^^ U
hithwen

2
4.1 के बाद से IterableUtils.toList(Iterable), वहाँ भी है , जो एक सुविधा पद्धति है और IteratorUtilsहुड के नीचे उपयोग करता है , लेकिन यह भी अशक्त (विपरीत IteratorUtils.toList) है।
योयो एन।

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, जैसा कि नाम से पता चलता है, यह एक के बाद एक पुनरावृत्त मानों को कॉपी करता है; यह देखने के बजाय एक प्रतिलिपि बनाता है।
टॉमाज़ नर्कविक्ज़

क्या अफ़सोस है कि CollectionUtilsएक अतिरिक्त पंक्ति में संग्रह के निर्माण को छोड़ने का कोई तरीका नहीं है ।
कार्ल रिक्टर

टूटी हुई लिंक ken
होला

14

हालांकि, यह मत भूलो कि सभी संग्रह परिमित हैं, जबकि Iterable के पास कोई वादा नहीं है। अगर कुछ Iterable है तो आप Iterator प्राप्त कर सकते हैं और वह यह है।

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

इसका विस्तार किया जाएगा:

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

it.hasNext () को कभी भी गलत वापस करने की आवश्यकता नहीं है। इस प्रकार सामान्य मामले में आप हर Iterable को एक संग्रह में बदलने में सक्षम होने की उम्मीद नहीं कर सकते। उदाहरण के लिए, आप सभी सकारात्मक प्राकृतिक संख्याओं पर पुनरावृत्ति कर सकते हैं, इसमें चक्र के साथ किसी चीज पर पुनरावृति कर सकते हैं जो बार-बार एक ही परिणाम उत्पन्न करता है, आदि।

वरना: अत्रे का जवाब काफी ठीक है।


1
क्या वास्तव में कभी कोई ऐसा व्यवहार करने योग्य है, जो किसी चीज पर अनंत हो (जैसे उत्तर में दिए गए प्राकृतिक संख्या उदाहरण), अभ्यास / वास्तविक कोड में? मुझे लगता है कि इस तरह के एक Iterable बहुत स्थानों में दर्द और परेशानी का कारण होगा ... :)
डेविड

2
@ दाविद हालांकि मैं अपने किसी भी उत्पादन कोड में विशेष रूप से एक अनंत Iterator को इंगित नहीं कर सकता, मैं उन मामलों के बारे में सोच सकता हूं जहां वे हो सकते हैं। एक वीडियो गेम में एक कौशल हो सकता है जो चक्रीय पैटर्न में आइटम बनाता है जो उपरोक्त उत्तर का सुझाव देता है। जबकि मैंने किसी भी अनंत पुनरावृत्तियों का सामना नहीं किया है, मैंने निश्चित रूप से पुनरावृत्तियों का सामना किया है जहाँ स्मृति एक वास्तविक चिंता है। मैं डिस्क पर फ़ाइलों पर पुनरावृत्तियों है। यदि मेरे पास पूर्ण 1TB डिस्क और 4GB RAM है, तो मैं आसानी से मेमोरी को अपने संग्रह में परिवर्तित करके चला सकता हूं।
रैडिक्डवर्ड 101

14

मैं FluentIterable.from(myIterable).toList()बहुत उपयोग करता हूं ।


9
ध्यान दिया जाना चाहिए कि यह अमरूद से भी है।
वाडज़िम

या org.apache.commons.collections4 से। फिर यह FluentIterable.of (myIterable) .toList ()
du-it

9

यह आपके प्रश्न का उत्तर नहीं है, लेकिन मेरा मानना ​​है कि यह आपकी समस्या का समाधान है। इंटरफ़ेस org.springframework.data.repository.CrudRepositoryमें वास्तव में ऐसे तरीके हैं जो वापस आते हैं java.lang.Iterableलेकिन आपको इस इंटरफ़ेस का उपयोग नहीं करना चाहिए। इसके बजाय अपने मामले में, उप इंटरफेस का उपयोग करें org.springframework.data.mongodb.repository.MongoRepository। इस इंटरफ़ेस में वे विधियाँ हैं जो टाइप की वस्तुएँ लौटाती हैं java.util.List


2
मैं एक सामान्य कार्यान्वयन के लिए अपने कोड को बाध्य करने से बचने के लिए सामान्य CrudRepository का उपयोग कर बढ़ावा दूंगा।
स्टेनलेक

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 nulls को अनुमति नहीं देता है जो अवांछनीय परिणाम प्रदान कर सकता है।

टेस्ट:

@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));
}

मैं संग्रह (सेट, सूची, आदि) के सभी उपप्रकारों के लिए समान उपयोगिताओं को लागू करता हूं। मुझे लगता है कि ये पहले से ही अमरूद का हिस्सा होंगे, लेकिन मुझे यह नहीं मिला।


1
आपका एक वर्ष पुराना उत्तर एक नए प्रश्न का आधार है stackoverflow.com/questions/32570534/… बहुत सारे विचारों और टिप्पणियों को आकर्षित करता है।
पॉल बोडिंगटन

6

जैसे ही आप फोन contains, containsAll, equals, hashCode, remove, retainAll, sizeयाtoArray , तुम वैसे भी तत्व को पार करना होगा।

यदि आप कभी-कभी केवल कॉलिंग विधियों जैसे कि isEmptyया clearमैं मान लें कि आप संग्रह को आलसी बनाकर बेहतर होंगे। आप उदाहरण के लिए एक समर्थन कर सकते हैंArrayList पहले से पुनरावृत्त तत्वों को संग्रहीत करने के लिए हैं।

मैं किसी भी पुस्तकालय में ऐसी किसी भी कक्षा के बारे में नहीं जानता, लेकिन इसे लिखने के लिए काफी सरल अभ्यास होना चाहिए।



6

जावा 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 शामिल करने के लिए कोई रास्ता है?
दिमित्री मिनकोवस्की

3
RxJava में अशक्त आइटम होने पर यह क्रैश हो जाता है। यह नहीं है?
एमबीएच

मेरा मानना ​​है कि RxJava2 अशक्त वस्तुओं की अनुमति नहीं देता है, RxJava में ठीक होना चाहिए।
डेरियसएल

4

जावा 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 को संग्रह में बदलने की आवश्यकता नहीं है - Iterable का उपयोग सीधे ऐसे लूप में किया जा सकता है, कोई वाक्यात्मक अंतर नहीं है, इसलिए मैं शायद ही समझ पाऊं कि मूल प्रश्न बिल्कुल क्यों पूछा गया था।
  2. Iterable to Collection को कनवर्ट करने का सुझाया गया तरीका असुरक्षित है (वही CollectionUtils से संबंधित है) - इस बात की कोई गारंटी नहीं है कि अगले () विधि के लिए बाद के कॉल अलग-अलग ऑब्जेक्ट इंस्टेंस को वापस करते हैं। इसके अलावा, यह चिंता शुद्ध सैद्धांतिक नहीं है। उदाहरण के लिए Iadoable Reducer की कम विधि में मानों को पारित करने के लिए उपयोग किया जाने वाला Iterable कार्यान्वयन हमेशा एक ही मान का उदाहरण देता है, बस विभिन्न फ़ील्ड मानों के साथ। इसलिए यदि आप ऊपर से मेकलेक्शन लागू करते हैं (या CollectionUtils.addAll (Iterator)) तो आप सभी समान तत्वों के साथ एक संग्रह के साथ समाप्त हो जाएंगे।

2

एक लाते समय मैं एक ऐसी ही स्थिति में आए 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

आप ग्रहण संग्रह कारखानों का उपयोग कर सकते हैं :

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);

तुम भी परिवर्तित कर सकते हैं Iterableएक करने के लिए LazyIterableऔर कनवर्टर तरीकों या अन्य उपलब्ध उपलब्ध एपीआई के किसी भी उपयोग करें।

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();

उपरोक्त सभी Mutableप्रकार विस्तारित होते हैंjava.util.Collection

नोट: मैं एक्लिप्स कलेक्शंस के लिए कमिट हूं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.