Dlaczego Iterator
interfejs się nie rozszerza Iterable
?
iterator()
Metoda może po prostu wrócić this
.
Czy jest to celowe czy tylko nadzór nad projektantami Javy?
Byłoby wygodnie móc użyć pętli dla każdego z iteratorami w następujący sposób:
for(Object o : someContainer.listSomeObjects()) {
....
}
gdzie listSomeObjects()
zwraca iterator.
Odpowiedzi:
Ponieważ iterator ogólnie wskazuje na pojedyncze wystąpienie w kolekcji. Iterowalność oznacza, że można uzyskać iterator z obiektu, aby przejść przez jego elementy - i nie ma potrzeby iteracji po pojedynczej instancji, co reprezentuje iterator.
źródło
Iterator jest stanowy. Chodzi o to, że jeśli zadzwonisz
Iterable.iterator()
dwukrotnie, otrzymasz niezależne iteratory - w każdym razie dla większości iteratorów. To oczywiście nie byłoby w twoim scenariuszu.Na przykład zazwyczaj mogę pisać:
To powinno wydrukować kolekcję dwa razy - ale przy twoim schemacie druga pętla zawsze kończy się natychmiast.
źródło
iterator
i użyjesz wyniku, musi on iterować kolekcję - czego nie zrobi, jeśli ten sam obiekt już wykonał iterację w kolekcji. Można dać żadnej poprawnej realizacji (inne niż do pustej kolekcji), gdzie ten sam iterator jest zwracany dwukrotnie?iterable
dwukrotne wywołanie daje niezależne iteratory.Jeśli chodzi o moje 0,02 USD, całkowicie zgadzam się, że Iterator nie powinien implementować Iterable, ale myślę, że ulepszona pętla for powinna zaakceptować oba. Myślę, że cały argument „iteratory iterable” pojawia się jako obejście wady języka.
Głównym powodem wprowadzenia ulepszonej pętli for było to, że „eliminuje ona znużenie i podatność na błędy iteratorów i zmiennych indeksowych podczas iteracji po kolekcjach i tablicach” [ 1 ].
Dlaczego zatem ten sam argument nie dotyczy iteratorów?
W obu przypadkach wywołania funkcji hasNext () i next () zostały usunięte, aw wewnętrznej pętli nie ma odwołania do iteratora. Tak, rozumiem, że iteratory mogą być ponownie użyte do utworzenia wielu iteratorów, ale wszystko dzieje się poza pętlą for: wewnątrz pętli występuje tylko jeden postęp w przód o jeden element w stosunku do elementów zwracanych przez iterator.
Umożliwienie tego również ułatwiłoby korzystanie z pętli for dla wyliczeń, które, jak wskazano w innym miejscu, są analogiczne do iteratorów, a nie iteratorów.
Więc nie zmuszaj Iteratora do implementacji Iterable, ale zaktualizuj pętlę for, aby zaakceptować oba.
Twoje zdrowie,
źródło
for(item : iter) {...}
składni, spowoduje to błąd, gdy iterator zostanie powtórzony dwukrotnie. Wyobraź sobie, żeIterator
jest przekazywany doiterateOver
metody zamiastIterable
w tym przykładzie .for (String x : strings) {...}
lubwhile (strings.hasNext()) {...}
styl: jeśli spróbujesz pętli przez dwukrotność czasu iteratora drugi przyniesie żadnych rezultatów, więc nie widzę, że sam w sobie jako argument przeciwko pozwalając składnię wzmocniona. Jon odpowiedź jest inna, bo on pokazuje jak owijaniaIterator
w sposóbIterable
mogłoby spowodować problemy, ponieważ w takim przypadku można oczekiwać, aby móc używać go tyle razy, ile ci się podoba.Jak zauważyli inni,
Iterator
iIterable
są dwie różne rzeczy.Ponadto wcześniejsze
Iterator
implementacje zostały ulepszone dla pętli.Przełamanie tego ograniczenia jest również trywialne za pomocą prostej metody adaptera, która wygląda tak, gdy jest używana z importem metody statycznej:
Przykładowa implementacja:
W Javie 8 adaptacja
Iterator
do naIterable
staje się prostsza:źródło
for (String s : (Iterable<String>) () -> iterator)
Jak powiedzieli inni, Iterable można wywołać wiele razy, zwracając nowy Iterator przy każdym wywołaniu; iterator jest używany tylko raz. Są więc powiązane, ale służą innym celom. Frustrujące jest jednak to, że metoda „compact for” działa tylko z iteracją.
To, co opiszę poniżej, to jeden ze sposobów na uzyskanie najlepszego z obu światów - zwracanie Iterable (dla lepszej składni), nawet gdy leżąca u podstaw sekwencja danych jest jednorazowa.
Sztuka polega na zwróceniu anonimowej implementacji Iterable, która faktycznie uruchamia pracę. Zamiast więc wykonać pracę, która generuje jednorazową sekwencję, a następnie zwrócić iterator, zwraca Iterable, który za każdym razem, gdy jest uzyskiwany dostęp, przerywa pracę. Może się to wydawać marnotrawstwem, ale często i tak wywołujesz Iterable tylko raz, a nawet jeśli wywołasz to wiele razy, nadal ma rozsądną semantykę (w przeciwieństwie do prostego opakowania, które sprawia, że Iterator „wygląda jak” Iterowalny, to wygrało ” nie powiedzie się, jeśli zostanie użyty dwukrotnie).
Powiedzmy na przykład, że mam DAO, która udostępnia serię obiektów z bazy danych, i chcę zapewnić do nich dostęp za pośrednictwem iteratora (np. Aby uniknąć tworzenia wszystkich obiektów w pamięci, jeśli nie są one potrzebne). Teraz mógłbym po prostu zwrócić iterator, ale to sprawia, że używanie zwracanej wartości w pętli jest brzydkie. Zamiast tego zawijam wszystko w anon Iterable:
można to następnie wykorzystać w kodzie:
co pozwala mi korzystać z kompaktowej pętli dla, jednocześnie zachowując przyrostowe użycie pamięci.
To podejście jest „leniwe” - praca nie jest wykonywana na żądanie Iterable, ale dopiero później, gdy zawartość jest iterowana - i musisz zdawać sobie sprawę z konsekwencji tego. W przykładzie z DAO oznacza to iterację wyników w ramach transakcji bazy danych.
Istnieją więc różne zastrzeżenia, ale w wielu przypadkach może to nadal być przydatnym idiomem.
źródło
returning a fresh Iterator on each call
powinieneś towarzyszyć dlaczego, tj. aby uniknąć problemu współbieżności ...Niesamowicie, nikt jeszcze nie udzielił tej odpowiedzi. Oto, w jaki sposób możesz „łatwo” iterować
Iterator
za pomocą nowejIterator.forEachRemaining()
metody Java 8 :Oczywiście istnieje „prostsze” rozwiązanie, które działa bezpośrednio z pętlą foreach, owijając ją
Iterator
wIterable
lambda:źródło
Iterator
to interfejs, który pozwala na iterację nad czymś. Jest to implementacja przechodzenia przez jakąś kolekcję.Iterable
to funkcjonalny interfejs, który oznacza, że coś zawiera dostępny iterator.W Javie 8 sprawia to, że życie jest dość łatwe ... Jeśli masz,
Iterator
ale potrzebujeszIterable
, możesz po prostu zrobić:Działa to również w pętli for:
źródło
Zgadzam się z przyjętą odpowiedzią, ale chcę dodać własne wyjaśnienie.
Iterator reprezentuje stan przejścia, np. Możesz pobrać bieżący element z iteratora i przejść do następnego.
Iterable reprezentuje kolekcję, którą można przemierzać, może zwrócić tyle iteratorów, ile chcesz, każda reprezentuje swój własny stan przejścia, jeden iterator może wskazywać na pierwszy element, a inny może wskazywać na element trzeci.
Byłoby miło, gdyby Java for loop akceptowała zarówno Iterator, jak i Iterable.
źródło
Aby uniknąć zależności od
java.util
pakietuZgodnie z oryginalnym JSR, Udoskonalona pętla for dla języka programowania Java ™ , proponowane interfejsy:
java.lang.Iterable
java.lang.ReadOnlyIterator
(proponowane do modernizacji
java.util.Iterator
, ale najwyraźniej tak się nigdy nie stało)… Zostały zaprojektowane tak, aby używać
java.lang
przestrzeni nazw pakietu zamiastjava.util
.Cytując JSR:
Nawiasem mówiąc, stary
java.util.Iterable
zyskał nowąforEach
metodę w Javie 8+, do użytku ze składnią lambda (przekazywanie aConsumer
).Oto przykład.
List
Interfejs obejmujeIterable
interfejs, jak każdy lista prowadziforEach
metody.źródło
Widzę też, że wielu to robi:
Ale to nie poprawia sytuacji! Ta metoda nie byłaby tym, czego chcesz!
Metoda
iterator()
ma zwrócić nowy iterator od zera. Trzeba więc zrobić coś takiego:Pytanie brzmi: czy byłby jakiś sposób, aby klasa abstrakcyjna wykonywała to? Aby uzyskać IterableIterator wystarczy zaimplementować dwie metody next () i hasNext ()
źródło
Jeśli przyszedłeś tu w poszukiwaniu obejścia, możesz użyć IteratorIterable . (dostępne dla Java 1.6 i nowszych)
Przykładowe użycie (odwrócenie wektora).
odciski
źródło
Dla uproszczenia Iterator i Iterable to dwa odrębne pojęcia. Iterable to po prostu skrót dla „Mogę zwrócić Iterator”. Myślę, że twój kod powinien być:
z instancją someContainer
SomeContainer extends Iterable<Object>
źródło
Nawiasem mówiąc: Scala ma metodę toIterable () w Iteratorze. Zobacz niejawną lub jawną konwersję Scala z iteratora do iterowalnego
źródło
W powiązanej notatce może być przydatny adapter IteratorIterable w Apache Commons Collections4. Wystarczy utworzyć instancję z iteratora, a otrzymasz odpowiednią iterację.
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html
ID: org.apache.commons: commons-collections4: 4.0
źródło
Iteratory są stanowe, mają „następny” element i stają się „wyczerpane” po iteracji. Aby zobaczyć, gdzie jest problem, uruchom następujący kod, ile liczb jest drukowanych?
źródło
Możesz wypróbować następujący przykład:
źródło