Bu arayüzlerin her ikisi de yalnızca bir yöntemi tanımlar
public operator fun iterator(): Iterator<T>
Belgelerde Sequence
tembel olması gerektiği yazıyor . Ama Iterable
tembel de değil Collection
mi (a tarafından desteklenmedikçe )?
Bu arayüzlerin her ikisi de yalnızca bir yöntemi tanımlar
public operator fun iterator(): Iterator<T>
Belgelerde Sequence
tembel olması gerektiği yazıyor . Ama Iterable
tembel de değil Collection
mi (a tarafından desteklenmedikçe )?
Yanıtlar:
Temel fark, anlambilimde ve stdlib uzantı işlevlerinin Iterable<T>
ve Sequence<T>
.
Çünkü Sequence<T>
uzantı işlevleri, Java Streams ara işlemlerine benzer şekilde mümkün olduğunda tembel olarak çalışır. Örneğin, Sequence<T>.map { ... }
başka bir tane döndürür Sequence<R>
ve veya benzeri bir uçbirim işlemi çağrılıncaya kadar öğeleri işlemez .toList
fold
Bu kodu düşünün:
val seq = sequenceOf(1, 2)
val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } // intermediate
print("before sum ")
val sum = seqMapped.sum() // terminal
Aşağıdakileri yazdırır:
before sum 1 2
Sequence<T>
terminalde yapılan işi azaltmak istediğinizde tembel kullanım ve verimli ardışık düzen için tasarlanmıştırJava Streams'de olduğu gibi işlemlerinde mümkün olduğunca . Bununla birlikte, tembellik, daha küçük koleksiyonların ortak basit dönüşümleri için istenmeyen bir durum olan ve onları daha az performanslı hale getiren bazı ek yükler getirir.
Genel olarak, ne zaman gerekli olduğunu belirlemenin iyi bir yolu yoktur, bu nedenle Kotlin stdlib'de tembellik açık hale getirilir ve varsayılan Sequence<T>
olarak tüm s'lerde kullanmaktan kaçınmak için arayüze çıkarılır Iterable
.
Bunun Iterable<T>
tersine, ara işlem semantiğine sahip uzantı işlevleri hevesle çalışır, öğeleri hemen işler ve bir başkasını döndürür Iterable
. Örneğin, içinde eşleme sonuçlarıyla birlikte Iterable<T>.map { ... }
bir döndürür List<R>
.
Yinelenebilir için eşdeğer kod:
val lst = listOf(1, 2)
val lstMapped: List<Int> = lst.map { print("$it "); it * it }
print("before sum ")
val sum = lstMapped.sum()
Bu çıktı:
1 2 before sum
Yukarıda belirtildiği gibi, Iterable<T>
varsayılan olarak tembel değildir ve bu çözüm kendini iyi gösterir: çoğu durumda iyi bir referans konumuna sahiptir, bu nedenle CPU önbelleğinden, tahmininden, önceden getirmeden vb. Yararlanır, böylece bir koleksiyonun birden fazla kopyalanması bile hala iyi çalışır. yeterli ve küçük koleksiyonlarla basit durumlarda daha iyi performans gösterir.
Değerlendirme işlem hattı üzerinde daha fazla kontrole ihtiyacınız varsa, Iterable<T>.asSequence()
işlevli tembel bir diziye açık bir dönüşüm vardır .
map
, filter
ve diğerleri gibi işlevler için ortak sözleşmeler , kaynak toplama türü dışında karar vermek için yeterli bilgi taşımaz ve çoğu koleksiyon aynı zamanda yinelenebilir olduğundan, bu "tembel olmak" için iyi bir işaret değildir çünkü genellikle HER YERDE. tembellik güvende olmak için açık olmalıdır.
Kısayol tuşunun cevabı tamamlanıyor:
Öğelerinizde Sıralı ve Yinelemeli yinelemenin nasıl olduğunu fark etmek önemlidir:
Sıra örneği:
list.asSequence().filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
Günlük sonucu:
filtre - Harita - Her biri; filtre - Harita - Her biri
Tekrarlanabilir örnek:
list.filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
filtre - filtre - Harita - Harita - Her biri - Her biri
Iterable
java.lang.Iterable
üzerindeki arayüze eşlenirJVM
ve List veya Set gibi yaygın olarak kullanılan koleksiyonlar tarafından uygulanır. Bunlar üzerindeki koleksiyon genişletme işlevleri hevesle değerlendirilir, yani hepsi girdilerindeki tüm öğeleri hemen işler ve sonucu içeren yeni bir koleksiyon döndürür.Aşağıda, yaşı en az 21 olan bir listedeki ilk beş kişinin adlarını almak için koleksiyon işlevlerini kullanmanın basit bir örneği verilmiştir:
val people: List<Person> = getPeople() val allowedEntrance = people .filter { it.age >= 21 } .map { it.name } .take(5)
Hedef platform: JVMRunning on kotlin v. 1.3.61 İlk olarak, listedeki her bir Kişi için yaş kontrolü yapılır ve sonuç yepyeni bir listeye eklenir. Ardından, filtre operatöründen sonra kalan her Kişi için adlarının eşleştirilmesi yapılır ve yeni bir liste daha elde edilir (bu şimdi a'dır
List<String>
). Son olarak, önceki listenin ilk beş öğesini içerecek şekilde oluşturulan son bir yeni liste var.Aksine, Sıra, Kotlin'de tembel olarak değerlendirilen bir değerler koleksiyonunu temsil eden yeni bir kavramdır.
Sequence
Arayüz için aynı koleksiyon uzantıları mevcuttur , ancak bunlar hemen tarihin işlenmiş durumunu temsil eden, ancak gerçekte herhangi bir öğeyi işlemeden Sekans örneklerini döndürür. İşlemeye başlamak içinSequence
, bir terminal operatörüyle sonlandırılması gerekir, bunlar temelde, temsil ettiği verileri somut bir biçimde somutlaştırmak için Diziye yapılan bir taleptir. Örnekler arasındatoList
,toSet
vesum
bunlardan sadece birkaçı vardır. Bunlar çağrıldığında, istenen sonucu elde etmek için yalnızca gereken minimum sayıda eleman işlenecektir.Mevcut bir koleksiyonu bir Sıraya dönüştürmek oldukça basittir, sadece
asSequence
uzantıyı kullanmanız gerekir . Yukarıda belirtildiği gibi, ayrıca bir terminal operatörü eklemeniz gerekir, aksi takdirde Sıra hiçbir zaman işlem yapmaz (yine tembel!)
val people: List<Person> = getPeople() val allowedEntrance = people.asSequence() .filter { it.age >= 21 } .map { it.name } .take(5) .toList()
Hedef platform: JVMRunning on kotlin v. 1.3.61 Bu durumda, Sıradaki Kişi örneklerinin her biri yaşlarına göre kontrol edilir, geçerlerse adları çıkarılır ve ardından sonuç listesine eklenir. Bu, beş kişi bulunana kadar orijinal listedeki her kişi için tekrarlanır. Bu noktada, toList işlevi bir liste döndürür ve içindeki kişilerin geri kalanı
Sequence
işlenmez.Bir Sıranın yapabileceği fazladan bir şey de vardır: sonsuz sayıda öğe içerebilir. Bu bakış açısıyla, operatörlerin çalıştıkları gibi çalışması mantıklıdır - sonsuz bir dizideki bir operatör, işini hevesle yaparsa asla geri dönemez.
Örnek olarak, terminal operatörünün gerektirdiği kadar 2'nin gücünü üretecek bir dizi (bunun hızlı bir şekilde taşacağı gerçeğini göz ardı ederek):
generateSequence(1) { n -> n * 2 } .take(20) .forEach(::println)
Daha fazlasını burada bulabilirsiniz .
Java
(çoğunluklaGuava
) hayranlar için büyük bir sürpriz