تفاوت Sequence و List در کاتلین
در این مقاله قصد داریم تا list و sequence را در کاتلین مورد بررسی قرار دهیم و تفاوت و بهینگی آنها را در موارد گوناگون مورد بررسی قرار دهیم.
همانطور که میدانیم٬ list در جاوا و یا کاتلین از iterator استفاده میکند. وقتی میخواهیم یک پردازش را روی لیست انجام دهیم٬ آن پردازش بر روی تمامی آیتم های آن لیست صورت میپذیرد.
Sequence شامل دو نوع پردازش Stateless و Stateful میشود. پردازش های stateless پردازش هایی هستند که نیازی به اطلاعاتی راجع به لیست ندارند.این امر باعث میشود که پردازش ها به صورت موازی انجام شود. برای مثال پردازش هایی مانند map و filter را میتوان در این دسته قرار داد. علت آن هم این است که چنین پردازشی بر روی تمام آیتم های لیست اعمال میشود. به این ترتیب چنین پردازشی را میتوان به عنوان پردازش موازی در نظر گرفت. اما در مقابل پردازش های stateful قرار دارند که پیش از پردازش باید اطلاعاتی پیرامون وضعیت فعلی لیست داشته باشند. sorted را میتوان از این دست پردازش ها در نظر گرفت.
اگر یک پردازش یک sequence جدید ایجاد کند٬ به آن intermediate و در غیر این صورت به آن terminal گفته میشود. اگر یک دسته از پردازش ها داشته باشیم که ترکیبی از پردازش های intermediate و terminal باشد٬ پردازش های intermediate ذخیره میشوند و در هنگام فراخوانی پردازش های ٬terminal پردازش های intermediate بر روی تک تک آیتم های باقیمانده اعمال میشود و در نهایت پردازش terminal انجام می شود.
برای مثال پردازش map از نوع پردازش های intermediate است و با نگاهی به کد آن مشخص میشود که پس از پردازش در یک sequence ذخیره میگردد:
/**
* Returns a sequence containing the results of applying the given [transform] function
* to each element in the original sequence.
*
* The operation is _intermediate_ and _stateless_.
*
* @sample samples.collections.Collections.Transformations.map
*/
public fun <T, R> Sequence<T>.map(transform: (T) -> R): Sequence<R> {
return TransformingSequence(this, transform)
}
اکنون برای مقایسه میان list و sequence یک قطعه کد را مورد بررسی قرار خواهیم داد. چنین کدی را در نظر بگیرید :
var i = 0
val items = listOf(2,4,50,40,90,22,11)
.map { println("operation map : ${++i}"); it * 2 }
.filter { println("operation filter : ${++i}"); it < 100 }
.first()
println(items)
در قطعه کد بالا٬ ابتدا تمام map ها انجام میشود و سپس filter بر روی آیتم های باقی مانده اعمال میشود. در نهایت اولین آیتم لیست ایجاد شده را انتخاب و نمایش میدهد. در نهایت با احتساب پردازش first تعداد پردازش های انجام شده ۱۵ پردازش است. اکنون این لیست را به sequence تبدیل میکنیم.
var i = 0
val items = listOf(2,4,50,40,90,22,11)
.asSequence()
.map { println("operation map : ${++i}"); it * 2 }
.filter { println("operation filter : ${++i}"); it < 100 }
.first()
println(items)
پس از اجرای کد فوق تعداد پردازش ها با احتساب first تعداد ۳ پردازش میشود. همانظور که مشاهده میکنید تعداد پردازش های کمتری انجام میشود. اکنون میتوانیم زمان انجام شده برای این دو پردازش را در یک لیست طولانی نیز بررسی کنیم:
var sum: Int = 0
val times = measureTimeMillis {
sum = (1..50000000)
.toList()
.map { it * 2 }
.filter { it % 3 == 0 }
.sum()
}
println(sum)
println(times / 1000f)
اگر کد فوق را اجرا کنیم٬ 38.766 ثانیه طول خواهد کشید. حال چنین پردازشی را روی یک sequence انجام میدهیم:
var sum: Int = 0
val times = measureTimeMillis {
sum = (1..50000000)
.asSequence()
.map { it * 2 }
.filter { it % 3 == 0 }
.sum()
}
println(sum)
println(times / 1000f)
و مدت زمان انجام آن 0.219 ثانیه خواهد شد. اما نتیجه پردازش هر دو برابر است. در اینجا باید این نکته را در نظر داشته باشیم که توالی پردازش ها نیز در مدت زمان پردازش حائز اهمیت است. اگر در پردازش فوق جای filter و map را باهم عوض کنیم٬ زمان پردازش برای list به 7.253 ثانیه کاهش مییابد.
در این مقاله قصد بر این شد تا با sequence ها آشنا شویم .
با ما همراه باشید.
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.