2013-05-08 16 views
9

Chcę zainicjować zestaw implementacji (HashSet) w Javie z Iterable. Jednak konstruktor HashSet nie akceptuje Iterables, lecz tylko obiekty typu Collections.Inicjowanie zestawu za pomocą Iterable

Czy istnieje sposób konwersji z Iterable na jakiś podtyp kolekcji.

Odpowiedz

6

HashSet Konstruktor opiera się na więcej niż to, co oferuje Iterable: chce poznać size kolekcji z przodu, aby optymalnie skonstruuj podstawę HashMap. Jeśli masz prawdziwe, surowe Iterable, które nie zna jego rozmiaru, musisz zdać sobie sprawę z Iterable z przodu, zmieniając go w zwykły Collection na wiele oczywistych sposobów.

Jeśli, z drugiej strony, masz bogatszy obiekt, który zna już jego rozmiar, zapłaciłaby za utworzenie minimalistycznej klasy adaptera, która opakowuje twój Iterable w kolekcję, realizując tylko size oprócz przekazywania połączenia do iterator.

public class IterableCollection<T> implements Collection<T> 
{ 
    private final Iterable<T> iterable; 

    public IterableCollection(Iterable<T> it) { this.iterable = it; } 

    @Override public Iterator<T> iterator() { return iterable.iterator(); } 

    @Override public int size() { return ... custom code to determine size ... } 

    @Override .... all others ... { throw new UnsupportedOperationException(); } 
} 
+0

Dobre wyjaśnienie. Myślę, że dostaję twój punkt i intuicję. – VaidAbhishek

2

Po prostu dodaj każdy.

public static <T> Set<T> setFromIterable(Iterable<T> i) { 
    HashSet<T> set = new HashSet<T>(); 
    Iterator<T> it = i.iterator(); 
    while (it.hasNext()) { 
     set.add(it.next()); 
    } 
    return set; 
} 

Iterable<Integer> someIterable = ...; 
Set<Integer> someSet = setFromIterable(someIterable); 

Zauważ, że ty nie użyć konstruktora new HashSet<Integer>(someIterator), dlatego, że nie istnieje. Po prostu wywołaj metodę statyczną.

+3

Myślę, że mylisz 'Iterable' i' Iterator'. –

+0

Tak, masz rację. Ale nawet iterator() nie działa. Obiekt, który mam, również oddaje Iterator(), ale nawet to nie jest akceptowalne dla konstruktora. – VaidAbhishek

+0

Kirk: masz rację; dzięki. @Vaid zobacz moją edycję. – wchargin

5

Oczywiście, jest to pokazane w this odpowiedzi. Zasadniczo, iteracyjne nad iterable i skopiować jego zawartość w kolekcji:

public static <T> List<T> copyIterable(Iterable<T> iterable) { 
    Iterator<T> iter = iterable.iterator(); 
    List<T> copy = new ArrayList<T>(); 
    while (iter.hasNext()) 
     copy.add(iter.next()); 
    return copy; 
} 

Użyj go w następujący sposób uzyskany List obiekt może być przekazany jako parametr do konstruktora HashSet.

Iterable<Integer> list = Arrays.asList(1, 2, 3); 
List<Integer> copy = copyIterable(list); 
Set<Integer> aSet = new HashSet<Integer>(copy); 

EDIT

Byłem pomylić cały czas. Iterable to superinterface z Collection, więc prosta (ale niebezpieczna) obsada zrobi lewę, o ile Iterable był na początku wymagany jako Collection.

Iterable<Integer> list = Arrays.asList(1, 2, 3); 
Set<Integer> aSet = new HashSet<Integer>((Collection)list); // it works! 
+0

Downovoter: czy chcesz skomentować? –

+0

Unikałem jednoznacznego powtarzania iteracji nad Iterable lub używania Iteratora.Poszukuję mechanizmu, który będzie akceptowalny dla konstruktora HashSet lub dowolnej implementacji zestawu. (BTW: Nie głosowałem na ciebie). Ale zdecydowanie bym cię przegłosował, jeśli możesz pokazać mi obejście. :-) – VaidAbhishek

+1

To akceptuje Iterator, a nie Iterable. – wchargin

32

Można użyć Guava.

Set<T> set = Sets.newHashSet(iterable); 

lub aby czytać jak zdanie statycznej importu

import static com.google.common.collect.Sets.*; 

Set<T> set = newHashSet(iterable); 
0

Interfejs Iterable pozwala składnia "foreach" do pracy, więc najczystszym sposobem jest prawdopodobne:

public <T> Set<T> toSet(Iterable<T> collection) { 
    HashSet<T> set = new HashSet<T>(); 
    for (T item: collection) 
     set.add(item); 
    return set; 
} 
Powiązane problemy