Stream API بر پایهی شیء خاصی به نام Spliterator استوار است. نام این رابط از این واقعیت آمده که نقش spliterator در Stream API شبیه نقش iterator در Collection API است. علاوه بر آن، چون Stream API پردازش موازی را پشتیبانی میکند، شیء spliterator همچنین کنترل میکند استریم چگونه عناصر خود را بین CPU های مختلفی که موازیسازی را مدیریت میکنند تقسیم میکند. نام آن ترکیبی از split و iterator است.
پوشش جزئی این شیء spliterator خارج از محدودهی این آموزش است. آنچه باید بدانید این است که این شیء spliterator ویژگیهای (characteristics) یک استریم را حمل میکند. این ویژگیها چیزی نیست که زیاد از آنها استفاده کنید، اما دانستن اینکه چه هستند در برخی موارد به نوشتن پایپلاینهای بهتر و کارآمدتر کمک میکند.
ویژگیهای یک استریم به شرح زیر هستند:
| Characteristic | Comment |
|---|---|
| ORDERED | ترتیب پردازش عناصر استریم مهم است. |
| DISTINCT | تکراری در عناصر پردازششده توسط آن استریم وجود ندارد. |
| NONNULL | هیچ عنصر null در آن استریم وجود ندارد. |
| SORTED | عناصر آن استریم مرتب شدهاند. |
| SIZED | تعداد عناصری که این استریم پردازش میکند مشخص است. |
| SUBSIZED | تجزیهی این استریم دو استریم SIZED تولید میکند. |
دو ویژگی دیگر، IMMUTABLE و CONCURRENT، در این آموزش پوشش داده نمیشوند.
هر استریم تمام این ویژگیها را هنگام ایجاد فعال یا غیرفعال دارد.
به یاد داشته باشید استریم به دو روش قابل ایجاد است:
ویژگیهای یک استریم مشخص به منبعی که از آن ایجاد شده بستگی دارد، یا به ویژگیهای استریمی که با آن ایجاد شده و نوع عملیاتی که آن را ایجاد کرده. اگر استریم شما با یک منبع ایجاد شده، ویژگیهایش به آن منبع بستگی دارد و اگر آن را با استریم دیگری ایجاد کردهاید، به آن استریم دیگر و نوع عملیاتی که استفاده میکنید بستگی دارد.
هر ویژگی در بیت خاصی از کلمهای که توسط spliterator حمل میشود ذخیره میشود. این کلمه توسط متد Spliterator.characteristics() برگردانده میشود. پس برای بررسی اینکه آیا استریم ویژگی مشخصی دارد یا نه، باید بررسی کنید آیا بیت مربوطه 0 است یا 1.
خوشبختانه، نیازی به انجام دستی این کار نیست چون رابط Spliterator متد Spliterator.hasCharacteristics() را به شما میدهد که یک ویژگی به عنوان پارامتر میگیرد.
مثال زیر predicate ای است که میتواند ویژگی ORDERED یک استریم را بررسی کند:
و این یکی همان کار را با فراخوانی متد Spliterator.hasCharacteristics() انجام میدهد.
اجرای کد قبلی خروجی یکسانی چاپ میکند.
ordered = true
اگر List را با Set در مثالهای قبلی جایگزین کنید، میبینید که ویژگی ORDERED دیگر وجود ندارد.
بیایید هر ویژگی را با جزئیات بیشتر معرفی کنیم.
استریمهای ORDERED از منابع مرتب داده ایجاد میشوند. اولین مثالی که به ذهن میرسد هر نمونهای از رابط List است. موارد دیگری هم هست: Files.lines(path) و Pattern.splitAsStream(string) نیز استریمهای ORDERED تولید میکنند.
حفظ ترتیب عناصر یک استریم میتواند برای استریمهای موازی overhead ایجاد کند. اگر به این ویژگی نیاز ندارید، میتوانید آن را با فراخوانی متد میانی unordered() روی استریم موجود حذف کنید. این استریم جدیدی بدون آن ویژگی برمیگرداند. چرا میخواهید این کار را انجام دهید؟ حفظ استریم ORDERED میتواند در برخی موارد پرهزینه باشد، مثلاً هنگام استفاده از استریمهای موازی.
استریم SORTED استریمی است که مرتب شده. این استریم میتواند از منبعی مرتب مانند نمونهی TreeSet ایجاد شده یا با فراخوانی متد sorted(). دانستن اینکه استریم قبلاً مرتب شده میتواند توسط پیادهسازی استریم استفاده شود تا از مرتبسازی دوبارهی استریمی که قبلاً مرتب شده جلوگیری کند.
عملیاتهای میانی وجود دارند که ویژگی SORTED را پاک میکنند. mapping یا flatmapping استریم SORTED این ویژگی را از استریم نتیجه حذف میکند.
اجرای کد قبلی خروجی زیر را چاپ میکند.
Is strings sorted? false
Is sortedStrings sorted? true
Is filtered sorted? true
Is lengths sorted? false
استریم DISTINCT استریمی است که تکراری در عناصر پردازششدهاش ندارد. این ویژگی هنگام ساختن استریم از HashSet یا با فراخوانی distinct() به دست میآید.
ویژگی DISTINCT هنگام فیلتر کردن حفظ میشود اما هنگام mapping یا flatmapping از بین میرود.
اجرای کد قبلی خروجی زیر را چاپ میکند.
Is strings distinct? false
Is distinct distinct? true
Is filtered distinct? true
Is lengths distinct? false
strings.stream() DISTINCT نیست چون از نمونهی List ساخته شده.strings.stream().distinct() DISTINCT است چون این استریم با فراخوانی متد میانی distinct() ایجاد شده.filtered همچنان DISTINCT است: حذف عناصر از استریم نمیتواند تکرار ایجاد کند.length نگاشت شده، پس ویژگی DISTINCT از بین رفته.استریم NONNULL استریمی است که مقدار null ندارد. ساختارهایی از Collection Framework وجود دارند که مقدار null نمیپذیرند، از جمله ArrayDeque و ساختارهای همزمان مثل ArrayBlockingQueue، ConcurrentSkipListSet، و مجموعهی همزمانی که با فراخوانی ConcurrentHashMap.newKeySet() برگردانده میشود. استریمهای ساختهشده با Files.lines(path) و Pattern.splitAsStream(line) نیز استریمهای NONNULL هستند.
فیلتر کردن یا مرتبسازی یک استریم NONNULL استریم NONNULL برمیگرداند. فراخوانی distinct() روی استریم NONNULL نیز استریم NONNULL برمیگرداند. اما mapping یا flatmapping استریم NONNULL استریمی بدون این ویژگی برمیگرداند.
این ویژگی آخر هنگام استفاده از استریمهای موازی بسیار مهم است.
استریم SIZED استریمی است که میداند چند عنصر پردازش خواهد کرد. استریم ساختهشده از هر نمونهای از Collection چنین استریمی است چون رابط Collection متد size() دارد.
از طرف دیگر، مواردی وجود دارد که میدانید استریم شما تعداد محدودی عنصر پردازش خواهد کرد، اما این تعداد را نمیتوانید مگر خود استریم را پردازش کنید بشناسید.
این مورد استریمهای ساختهشده با الگوی Files.lines(path) است. میتوانید اندازهی فایل متنی را به بایت بدست آورید، اما این اطلاعات به شما نمیگوید این فایل متنی چند خط دارد. برای بدست آوردن این اطلاعات باید فایل را تحلیل کنید.
همین مورد برای الگوی Pattern.splitAsStream(line) نیز صادق است.
ویژگی SUBSIZED مربوط به نحوهی تجزیهی استریم هنگام محاسبهی موازی است. به زبان ساده، مکانیزم موازیسازی استریم را به دو بخش تقسیم و محاسبه را بین هستههای مختلف موجود توزیع میکند. این تجزیه توسط نمونهی Spliterator که استریم استفاده میکند پیادهسازی میشود. این پیادهسازی به منبع دادهای که استفاده میکنید بستگی دارد.
فرض کنید نیاز دارید استریمی روی ArrayList باز کنید. تمام دادههای این لیست در آرایهی داخلی نمونهی ArrayList شما نگهداری میشود. شاید به یاد داشته باشید آرایهی داخلی شیء ArrayList آرایهای متراکم است چون وقتی عنصری از این آرایه حذف میشود تمام عناصر بعدی یک سلول به چپ حرکت میکنند تا حفرهای باقی نماند.
این تجزیهی ArrayList را ساده میکند. برای تجزیهی نمونهای از ArrayList، فقط کافی است این آرایهی داخلی را به دو بخش با تعداد عناصر یکسان تقسیم کنید. این استریم ساختهشده روی نمونهای از ArrayList را SUBSIZED میکند.
اکنون فرض کنید نیاز دارید استریمی روی نمونهای از HashSet باز کنید. HashSet عناصرش را در آرایهای ذخیره میکند، اما این آرایه متفاوت از ArrayList استفاده میشود. در واقع، بیش از یک عنصر میتواند در سلون مشخصی از این آرایه ذخیره شود. تجزیهی این آرایه بدون شمردن عناصر ممکن نیست. به همین دلیل استریم ساختهشده روی نمونهای از HashSet SIZED است اما SUBSIZED نیست.
تبدیل یک استریم میتواند ویژگیهای SIZED و SUBSIZED استریم برگرداندهشده را تغییر دهد.
distinct() این ویژگیها را پاک میکند.برای محاسبات موازی، داشتن استریمهای SIZED و SUBSIZED همیشه بهتر است.
اجرای کد قبلی خروجی زیر را چاپ میکند.
Array list is sized? true
Array list is subsized? true
اجرای کد قبلی خروجی زیر را چاپ میکند.
Hash set is sized? true
Hash set is subsized? false
اجرای کد قبلی خروجی زیر را چاپ میکند.
Pattern split as stream is sized? false
Pattern split as stream is subsized? false
اجرای کد قبلی خروجی زیر را چاپ میکند.
Pattern split as stream is sized? false
Pattern split as stream is subsized? false
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/