Oba te interfejsy definiują tylko jedną metodę
public operator fun iterator(): Iterator<T>
Dokumentacja mówi, Sequence
że ma być leniwy. Ale czy nie jest Iterable
też leniwy (chyba że jest poparty przez Collection
)?
źródło
Oba te interfejsy definiują tylko jedną metodę
public operator fun iterator(): Iterator<T>
Dokumentacja mówi, Sequence
że ma być leniwy. Ale czy nie jest Iterable
też leniwy (chyba że jest poparty przez Collection
)?
Kluczowa różnica polega na semantyce i implementacji funkcji rozszerzających stdlib dla Iterable<T>
i Sequence<T>
.
W przypadku Sequence<T>
, gdy to możliwe, funkcje rozszerzeń działają leniwie, podobnie jak pośrednie operacje strumieni Java . Na przykład Sequence<T>.map { ... }
zwraca inny Sequence<R>
i faktycznie nie przetwarza elementów, dopóki nie zostanie wywołana operacja terminala , taka jak toList
lub fold
.
Rozważ ten kod:
val seq = sequenceOf(1, 2)
val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } // intermediate
print("before sum ")
val sum = seqMapped.sum() // terminal
Drukuje:
before sum 1 2
Sequence<T>
jest przeznaczony do leniwego użytkowania i wydajnego przetwarzania potokowego, gdy chcesz maksymalnie zredukować pracę wykonywaną w operacjach terminalowych , tak samo jak w przypadku strumieni Java. Jednak lenistwo wprowadza pewien narzut, co jest niepożądane w przypadku zwykłych prostych przekształceń mniejszych kolekcji i sprawia, że są mniej wydajne.
Ogólnie rzecz biorąc, nie ma dobrego sposobu na określenie, kiedy jest to potrzebne, więc w Kotlin stdlib lenistwo jest jawne i wyodrębniane do Sequence<T>
interfejsu, aby uniknąć używania go Iterable
domyślnie na wszystkich s.
Na Iterable<T>
, na Przeciwnie, funkcje Przedłużacz z pośrednich semantyki operacyjnych pracują gorliwie, przetwarzać elementy od razu i powrócić inny Iterable
. Na przykład Iterable<T>.map { ... }
zwraca a List<R>
z wynikami mapowania.
Odpowiedni kod dla Iterable:
val lst = listOf(1, 2)
val lstMapped: List<Int> = lst.map { print("$it "); it * it }
print("before sum ")
val sum = lstMapped.sum()
To drukuje:
1 2 before sum
Jak wspomniano powyżej, Iterable<T>
domyślnie nie jest leniwy, a to rozwiązanie pokazuje się dobrze: w większości przypadków ma dobrą lokalizację odniesienia, dzięki czemu wykorzystuje pamięć podręczną procesora, przewidywanie, wstępne pobieranie itp., Więc nawet wielokrotne kopiowanie kolekcji nadal działa dobrze wystarczająco dużo i działa lepiej w prostych przypadkach z małymi zbiorami.
Jeśli potrzebujesz większej kontroli nad potokiem oceny, istnieje jawna konwersja do leniwej sekwencji z Iterable<T>.asSequence()
funkcją function.
Java
(głównieGuava
) fanówmap
,filter
a inni nie posiadają wystarczających informacji, aby zdecydować, inne niż z rodzaju Źródło, a ponieważ większość kolekcje są również iterable, że nie jest dobrym markerem „leń”, ponieważ jest powszechnie WSZĘDZIE. leniwy musi być wyraźny, aby był bezpieczny.Uzupełnianie odpowiedzi klawisza skrótu:
Ważne jest, aby zauważyć, jak sekwencja i iteracja iterują w twoich elementach:
Przykład sekwencji:
list.asSequence().filter { field -> Log.d("Filter", "filter") field.value > 0 }.map { Log.d("Map", "Map") }.forEach { Log.d("Each", "Each") }
Wynik dziennika:
filtr - Mapa - Każdy; filtr - Mapa - Każdy
Przykład iterowalny:
list.filter { field -> Log.d("Filter", "filter") field.value > 0 }.map { Log.d("Map", "Map") }.forEach { Log.d("Each", "Each") }
filtr - filtr - Mapa - Mapa - Każda - Każda
źródło
Więcej znajdziesz tutaj .
źródło