preloder
دانستنی‌ها

آگاهانه از findFirst و findAny استفاده کنید.

بعد از فیلتر کردن یک stream جاوا ۸ بسیار رایج است که از findFirst() یا findAny() برای دریافت المانی که بعد از فیلتر شدن باقی‌مانده است استفاده کنیم. اما این کاری که شما می‌خواهید را دقیقا انجام نمی‌دهد و خطاهای کوچکی ممکن است رخ دهد.


اما مشکل findFirst() و findAny() چیست؟‌

همانطور که در جاواداک آن‌ها قابل مشاهده است هر دو تابع یک المان دلخواه از استریم را برمی‌گردانند مگر اینکه استریم ترتیب وقوعی داشته باشد که در آن زمان findFirst() اولین المان را برمی‌گرداند. به همین سادگی.

یک مثال ساده آن به شکل زیر است:

که مشابه نسخه قدیمی حلقه for ان به شکل زیر است:

اما هر دو نمونه یک خطای احتمالی می‌توانند داشته باشند: هر دو فرض کرده‌اند که تنها یک مشتری با ID داده شده وجود دارد.

این فرض ممکن است منطقی باشد. شاید این یک محدودیت شناخته شده است که توسط بخش‌های اختصاصی سیستم کنترل می‌شود. در این صورت به طور کلی درست است.

اما در بسیاری از شرایط اینطور نیست. ممکن است که مشتری از یک منبع خارجی بارگذاری شود که هیچ ضمانتی به یکتا بودن شناسه آن نیست. ممکن است این خطا اجازه دهد دو کتاب با شابک یکسانی وجود داشته باشند. ممکن است جستجو نتایج پیش‌بینی نشده‌ای داشته باشد.

اغلب صحت کدها بر مبنای فرضیاتی است که در یک حوزه تنها یک المان یکتا مطابقت پیدا می‌کند اما هیچ کاری برای اعلان یا اعمال چنین محدودیتی انجام نمی‌دهند.

بدتر از آن اینکه این اشتباه رفتاری کاملا مبتنی بر داده است که ممکن است در حین تست مخفی بماند. مگر اینکه چنین سناریویی در ذهن داشته باشیم که در آن صورت قبل از تولید محصول نهایی آن را در نظر می‌گیریم.

حتی بدتر از آن اینکه خیلی ساکت با شکست روبرو می‌شود! اگر این فرض که تنها یک المان وجود دارد نقض شود، ما به طور مستقیم متوجه آن نمی‌شویم. به جای آن سیستم رفتار درستی برای مدتی نشان نمی‌دهد تا زمانی که علت آن کشف شود.

مسلما مشکلی با ذات توابع findFirst() و findAny() وجود ندارد. اما خیلی ساده می‌توانند مورد استفاده قرار گیرند که منجر به باگ در منطق مدل‌سازی دامنه شوند.

شکست زودهنگام

حال بیایید مشکل را برطرف کنیم! فرض کنیم که تقریبا مطمئن هستیم که حداکثر یک المان با خواسته ما مطابقت دارد و می‌خواهیم در صورت نقض این فرض به سرعت با شکست مواجه شویم. در یک حلقه نیاز به مدیریت وضعیت‌های نامناسب خواهیم داشت و برنامه به شکل زیر خواهد بود:

 

حالا استریم‌ها راه بهتری در اختیار ما قرار می‌دهند. ما می‌توانیم از reduce استفاده کنیم که طبق مستندات آن، یک کاهش روی المان‌های رشته با استفاده از تابع تجمع انجمنی (associative accumulation function) انجام می‌دهد و یک Optional برمی‌گرداند که مقدار کاهش یافته را، در صورت وجود، توصیف می‌کند. در کد زیر که کاری معادل reduce انجام می‌دهد، توضیحات داده شده را بهتر می‌توانید درک کنید:

اما در reduce لازم نیست که به ترتیب اجرا شود.

این مشابه حلقه for بالا نیست؟! چه تصادفی..!

پس همه چیزی که نیاز داریم یک انباشتگر (accumulator) است که خطای مورد نظر را به محض فراخوانی پرتاب می‌کند:

اندکی عجیب به نظر می‌رسد اما کاری که می‌خواهیم را انجام می‌دهد. برای خواناتر کردن آن، آن را در یک استریم utility class قرار می‌دهیم و نام مناسبی به آن می‌دهیم.

پس می‌توانیم به شکل زیر فراخوانی کنیم:

باید توجه شود که برخلاف findFirst() و findAny() این یک عملگر short-circuit نیست و کل استریم را محقق می‌سازد. همانطور در صورتی که تنها یک المان باشد. پردازش به محض اینکه المان دوم پیدا شود متوقف می‌گردد.

نتیجه

ما دیدیم که چرا findFirst() و findAny() برای بیان فرض اینکه تنها یک المان در استریم وجود دارند کافی نیستند. برای بیان این فرض و اطمینان از اینکه در صورت نقض آن کد به سرعت با شکست مواجه می‌شود، لازم است از reduce(toOnlyElement()) استفاده کنیم.

منبع:

https://jaxenter.com/

[تعداد: 0    میانگین: 0/5]
برچسب ها
نمایش بیشتر

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

همچنین ببینید

بستن
دکمه بازگشت به بالا