JavaRush /وبلاگ جاوا /Random-FA /قهوه استراحت شماره 177. راهنمای تفصیلی جاوا استریم در جاو...

قهوه استراحت شماره 177. راهنمای تفصیلی جاوا استریم در جاوا 8

در گروه منتشر شد
منبع: Hackernoon در این پست آموزش کار با جاوا استریم به همراه مثال کد و توضیحات ارائه شده است. قهوه استراحت شماره 177.  راهنمای تفصیلی جاوا استریم در جاوا 8 - 1

آشنایی با موضوعات جاوا در جاوا 8

جاوا استریم که به عنوان بخشی از جاوا 8 معرفی شده است، برای کار با مجموعه داده ها استفاده می شود. آنها به خودی خود یک ساختار داده نیستند، اما می توان از آنها برای وارد کردن اطلاعات از سایر ساختارهای داده با سفارش و خط لوله برای ایجاد نتیجه نهایی استفاده کرد. نکته: مهم است که Stream و Thread را اشتباه نگیرید، زیرا در روسی هر دو اصطلاح اغلب در ترجمه یکسانی "جریان" ذکر می شوند. Stream یک شی را برای انجام عملیات (اغلب انتقال یا ذخیره داده) نشان می دهد، در حالی که Thread (ترجمه تحت اللفظی - رشته) به شیئی اشاره می کند که به کد برنامه خاصی اجازه می دهد تا به موازات سایر شاخه های کد اجرا شود. از آنجا که Stream یک ساختار داده جداگانه نیست، هرگز منبع داده را تغییر نمی دهد. جریان های جاوا دارای ویژگی های زیر هستند:
  1. جاوا استریم را می توان با استفاده از بسته "java.util.stream" استفاده کرد. می توان آن را با استفاده از کد زیر به یک اسکریپت وارد کرد:

    import java.util.stream.* ;

    همچنین با استفاده از این کد می توانیم چندین تابع داخلی را در جاوا استریم به راحتی پیاده سازی کنیم.

  2. جاوا استریم می‌تواند ورودی‌های مجموعه‌های داده مانند مجموعه‌ها و آرایه‌ها را در جاوا بپذیرد.

  3. جاوا استریم نیازی به تغییر ساختار داده ورودی ندارد.

  4. جاوا استریم منبع را تغییر نمی دهد. در عوض، خروجی را با استفاده از روش های خط لوله مناسب تولید می کند.

  5. جاوا استریم ها تابع عملیات میانی و پایانی هستند که در قسمت های بعدی به آنها خواهیم پرداخت.

  6. در جاوا استریم، عملیات میانی خط لوله می شوند و در قالب ارزیابی تنبلی رخ می دهند. آنها با توابع ترمینال پایان می یابند. این فرمت اصلی برای استفاده از جاوا استریم را تشکیل می دهد.

در بخش بعدی، به روش‌های مختلف مورد استفاده در جاوا 8 برای ایجاد جریان جاوا در صورت لزوم و در صورت لزوم نگاه خواهیم کرد.

ایجاد جریان جاوا در جاوا 8

رشته های جاوا را می توان به روش های مختلفی ایجاد کرد:

1. ایجاد یک جریان خالی با استفاده از متد ()Stream.empty

می‌توانید یک جریان خالی برای استفاده بعداً در کد خود ایجاد کنید. اگر از متد Stream.empty() استفاده کنید ، یک جریان خالی تولید می‌شود که هیچ مقداری ندارد. اگر بخواهیم در زمان اجرا از یک استثنای اشاره گر تهی بگذریم، این جریان خالی می تواند مفید باشد. برای این کار می توانید از دستور زیر استفاده کنید:
Stream<String> str = Stream.empty();
دستور بالا یک جریان خالی به نام str را بدون هیچ عنصری در داخل آن ایجاد می کند. برای تأیید این موضوع، فقط تعداد یا اندازه جریان را با استفاده از عبارت str.count() بررسی کنید . مثلا،
System.out.println(str.count());
در نتیجه، در خروجی 0 می گیریم .

2. با استفاده از متد Stream.builder() با یک نمونه Stream.Builder یک جریان ایجاد کنید.

همچنین می‌توانیم از Stream Builder برای ایجاد یک جریان با استفاده از الگوی طراحی سازنده استفاده کنیم. برای ساخت گام به گام اشیا طراحی شده است. بیایید ببینیم چگونه می‌توانیم یک جریان را با استفاده از Stream Builder نمونه‌سازی کنیم .
Stream.Builder<Integer> numBuilder = Stream.builder();

numBuilder.add(1).add(2).add( 3);

Stream<Integer> numStream = numBuilder.build();
با استفاده از این کد، می توانید یک جریان به نام numStream حاوی عناصر int ایجاد کنید . همه چیز به لطف نمونه Stream.Builder به نام numBuilder که ابتدا ایجاد شد، بسیار سریع انجام می شود.

3. با استفاده از متد Stream.of() یک جریان با مقادیر مشخص شده ایجاد کنید

راه دیگر برای ایجاد یک جریان شامل استفاده از متد Stream.of() است . این یک راه ساده برای ایجاد یک جریان با مقادیر داده شده است. این موضوع را اعلام می کند و همچنین مقداردهی اولیه می کند. مثالی از استفاده از متد Stream.of() برای ایجاد یک جریان:
Stream<Integer> numStream = Stream.of(1, 2, 3);
این کد یک جریان حاوی عناصر int ایجاد می کند ، درست مانند روش قبلی با استفاده از Stream.Builder . در اینجا ما مستقیماً یک جریان با استفاده از Stream.of() با مقادیر از پیش تعریف شده [1، 2 و 3] ایجاد کرده ایم .

4. یک جریان از یک آرایه موجود با استفاده از متد ()Arrays.stream ایجاد کنید

یکی دیگر از روش های رایج برای ایجاد یک رشته، استفاده از آرایه ها در جاوا است. جریان در اینجا از یک آرایه موجود با استفاده از متد ()Arrays.stream ایجاد می‌شود . همه عناصر آرایه به عناصر جریان تبدیل می شوند. در اینجا یک مثال خوب است:
Integer[] arr = {1, 2, 3, 4, 5};

Stream<Integer> numStream = Arrays.stream(arr);
این کد یک numStream حاوی محتویات آرایه ای به نام arr که یک آرایه عدد صحیح است تولید می کند.

5. ادغام دو جریان موجود با استفاده از متد Stream.concat

روش دیگری که می توان برای ایجاد یک جریان استفاده کرد، متد Stream.concat () است . از آن برای ترکیب دو رشته برای ایجاد یک رشته استفاده می شود. هر دو جریان به ترتیب ترکیب شده اند. این بدان معناست که نخ اول اول می آید و به دنبال آن نخ دوم و غیره. نمونه ای از چنین الحاقی به این صورت است:
Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);

Stream<Integer> numStream2 = Stream.of(1, 2, 3);

Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
عبارت فوق یک جریان نهایی به نام combinationdStream ایجاد می کند که حاوی عناصر اولین جریان numStream1 و جریان دوم numStream2 یک به یک است .

انواع عملیات با جاوا استریم

همانطور که قبلا ذکر شد، می توانید دو نوع عملیات را با Java Stream در جاوا 8 انجام دهید: میانی و ترمینال. بیایید به هر یک از آنها با جزئیات بیشتری نگاه کنیم.

عملیات میانی

عملیات میانی یک جریان خروجی تولید می کند و تنها در صورت مواجهه با عملیات ترمینال اجرا می شود. این بدان معنی است که عملیات میانی به صورت تنبل اجرا می شوند، خط لوله انجام می شوند، و تنها می توانند با عملیات ترمینال تکمیل شوند. کمی بعد در مورد ارزیابی تنبل و خط لوله یاد خواهید گرفت. نمونه هایی از عملیات میانی عبارتند از: filter() , map() , different() , peek() , sorted() و برخی دیگر.

عملیات ترمینال

عملیات ترمینال اجرای عملیات میانی را تکمیل می کند و همچنین نتایج نهایی جریان خروجی را برمی گرداند. از آنجایی که عملیات ترمینال پایان اجرای تنبل و خط لوله را نشان می دهد، این رشته نمی تواند پس از انجام عملیات ترمینال دوباره مورد استفاده قرار گیرد. نمونه هایی از عملیات ترمینال متدهای زیر هستند: forEach() ، collect() ، count() ، reduce() و غیره.

نمونه هایی از عملیات با جاوا استریم

عملیات میانی

در اینجا چند نمونه از برخی از عملیات میانی که می توانند در جریان جاوا اعمال شوند آورده شده است:

فیلتر()

این روش برای فیلتر کردن عناصر جریانی که با یک گزاره خاص در جاوا مطابقت دارند استفاده می شود. این موارد فیلتر شده سپس جریان جدیدی را تشکیل می دهند. برای درک بهتر به یک مثال نگاه می کنیم.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
نتیجه:
[98]
توضیح: در این مثال می بینید که حتی عناصر (قابل تقسیم بر 2) با استفاده از روش filter() فیلتر شده و در یک لیست عدد صحیح numStream ذخیره می شوند که محتویات آن بعداً چاپ می شود. از آنجایی که 98 تنها عدد صحیح در جریان است، در خروجی چاپ می شود.

نقشه ()

این روش برای ایجاد یک جریان جدید با اجرای توابع نگاشت شده بر روی عناصر جریان ورودی اصلی استفاده می شود. شاید جریان جدید نوع داده متفاوتی داشته باشد. مثال به این صورت است:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
نتیجه:
[86، 130، 2، 196، 126]
توضیح: در اینجا می بینیم که از متد map() برای دو برابر کردن هر عنصر از جریان numStream استفاده می شود . همانطور که در خروجی مشاهده می کنید، هر یک از عناصر موجود در جریان با موفقیت دو برابر شده است.

متمایز()

این روش فقط برای بازیابی عناصر منفرد در یک جریان با فیلتر کردن موارد تکراری استفاده می شود. نمونه ای از آن به شکل زیر است:
Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
نتیجه:
[43، 65، 1، 98، 63]
توضیح: در این مورد، متد different() برای numStream استفاده می شود . با حذف موارد تکراری از جریان، تمام عناصر منفرد در numList را بازیابی می کند . همانطور که از خروجی می بینید، برخلاف جریان ورودی که در ابتدا دو تکراری (63 و 1) داشت، هیچ تکراری وجود ندارد.

زیرچشمی نگاه کردن()

این روش برای ردیابی تغییرات میانی قبل از اجرای عملیات ترمینال استفاده می شود. این بدان معنی است که peek() می تواند برای انجام یک عملیات بر روی هر عنصر یک جریان استفاده شود تا جریانی ایجاد شود که عملیات میانی بیشتری را می توان روی آن انجام داد.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
نتیجه:
نقشه برداری: 430 نقشه برداری: 650 نقشه برداری: 10 نقشه برداری: 980 نقشه برداری: 630 [430, 650, 10, 980, 630]
توضیح: در اینجا از متد peek() برای تولید نتایج میانی استفاده می شود زیرا متد map() روی عناصر جریان اعمال می شود. در اینجا می‌توانیم متوجه شویم که حتی قبل از استفاده از عملیات ترمینال collect() برای چاپ محتویات نهایی لیست در دستور چاپ ، نتیجه نگاشت هر عنصر جریان به‌طور متوالی از قبل چاپ می‌شود.

مرتب شده ()

متد sorted() برای مرتب سازی عناصر یک جریان استفاده می شود. به طور پیش فرض، عناصر را به ترتیب صعودی مرتب می کند. همچنین می توانید ترتیب مرتب سازی خاصی را به عنوان پارامتر مشخص کنید. نمونه ای از پیاده سازی این روش به شکل زیر است:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
نتیجه:
1 43 ​​63 65 98
توضیح: در اینجا از متد sorted() برای مرتب‌سازی عناصر جریان به ترتیب صعودی به‌طور پیش‌فرض استفاده می‌شود (زیرا ترتیب خاصی مشخص نشده است). می بینید که موارد چاپ شده در خروجی به ترتیب صعودی مرتب شده اند.

عملیات ترمینال

در اینجا چند نمونه از برخی عملیات ترمینال که می توانند در جریان های جاوا اعمال شوند آورده شده است:

برای هر()

متد forEach() برای تکرار در تمام عناصر یک جریان و اجرای تابع روی هر عنصر یک به یک استفاده می شود. این به عنوان جایگزینی برای دستورات حلقه مانند for ، while و دیگران عمل می کند. مثال:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
نتیجه:
43 65 1 98 63
توضیح: در اینجا از متد forEach() برای چاپ تک تک عناصر جریان استفاده می شود.

شمردن()

متد count() برای بازیابی تعداد کل عناصر موجود در جریان استفاده می شود. شبیه به متد size() است که اغلب برای تعیین تعداد کل عناصر در یک مجموعه استفاده می شود. نمونه ای از استفاده از متد count() با جریان جاوا به شرح زیر است:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
نتیجه:
5
توضیح: از آنجایی که numStream شامل 5 عنصر صحیح است، با استفاده از متد count() روی آن خروجی 5 خواهد شد.

جمع آوری ()

متد collect() برای انجام کاهش های قابل تغییر عناصر جریان استفاده می شود. می توان از آن برای حذف محتوا از جریان پس از تکمیل پردازش استفاده کرد. از کلاس Collector برای انجام کاهش استفاده می کند .
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
نتیجه:
[43، 65، 1، 63]
توضیح: در این مثال، تمام عناصر عجیب و غریب در جریان فیلتر شده و در لیستی به نام odd جمع‌آوری/کاهش می‌شوند . در پایان، لیستی از موارد فرد چاپ می شود.

min() و max()

روش min() همانطور که از نام آن پیداست، می تواند در یک جریان برای یافتن حداقل عنصر در آن استفاده شود. به طور مشابه، روش max() می تواند برای یافتن حداکثر عنصر در یک جریان استفاده شود. بیایید سعی کنیم بفهمیم که چگونه می توان از آنها با یک مثال استفاده کرد:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
نتیجه:
کوچکترین عنصر: 1 بزرگترین عنصر: 98
توضیح: در این مثال، ما کوچکترین عنصر در numStream را با استفاده از روش min() و بزرگترین عنصر را با استفاده از متد max() چاپ کردیم . توجه داشته باشید که در اینجا، قبل از استفاده از متد max() ، ما دوباره عناصری را به numStream اضافه کرده‌ایم . این به این دلیل است که min() یک عملیات ترمینال است و محتویات جریان اصلی را از بین می برد و فقط نتیجه نهایی را برمی گرداند (که در این مورد عدد صحیح "کوچکترین" بود).

findAny() و findFirst()

findAny() هر عنصری از جریان را به عنوان اختیاری برمی‌گرداند . اگر جریان خالی باشد، یک مقدار اختیاری را نیز برمی‌گرداند که خالی خواهد بود. findFirst() اولین عنصر جریان را به صورت اختیاری برمی گرداند . همانند متد findAny() ، متد findFirst() نیز در صورتی که جریان مربوطه خالی باشد، یک پارامتر اختیاری خالی برمی گرداند. بیایید به مثال زیر بر اساس این روش ها نگاهی بیندازیم:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
نتیجه:
اختیاری[43] اختیاری.خالی
توضیح: در اینجا، در حالت اول، متد findFirst() اولین عنصر جریان را به صورت Optional برمی گرداند . سپس، زمانی که رشته مجدداً به عنوان یک رشته خالی تخصیص داده می شود، متد findAny() یک Optional خالی برمی گرداند .

allMatch() ، anyMatch() و noneMatch()

متد allMatch() برای بررسی اینکه آیا همه عناصر یک جریان با یک گزاره خاص مطابقت دارند یا خیر استفاده می شود و در صورت وجود مقدار بولی true را برمی گرداند، در غیر این صورت false را برمی گرداند . اگر جریان خالی باشد، true را برمی‌گرداند . متد anyMatch() برای بررسی اینکه آیا هر یک از عناصر موجود در جریان با یک محمول خاص مطابقت دارد یا خیر استفاده می شود. اگر چنین است true ، در غیر این صورت false را برمی گرداند . اگر جریان خالی باشد، false را برمی‌گرداند . اگر هیچ عنصری در جریان با گزاره مطابقت نداشته باشد، متد noneMatch() true را برمی‌گرداند و در غیر این صورت false . یک مثال برای نشان دادن این به این شکل است:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
نتیجه:
نادرست درست نادرست
توضیح: برای یک جریان numStream حاوی 1 به عنوان یک عنصر، متد allMatch() false را برمی گرداند زیرا همه عناصر 1 نیستند، بلکه فقط یکی از آنها است. متد anyMatch() true را برمی گرداند زیرا حداقل یکی از عناصر 1 است. متد noneMatch() false را برمی گرداند زیرا 1 در واقع به عنوان یک عنصر در این جریان وجود دارد.

ارزیابی تنبل در جاوا استریم

ارزیابی تنبل منجر به بهینه‌سازی در هنگام کار با جاوا استریم در جاوا 8 می‌شود. آنها عمدتاً شامل به تأخیر انداختن عملیات میانی تا زمانی که با یک عملیات ترمینال مواجه می‌شوند، می‌شوند. ارزیابی تنبل مسئول جلوگیری از اتلاف غیرضروری منابع در محاسبات است تا زمانی که نتیجه واقعاً مورد نیاز باشد. جریان خروجی حاصل از عملیات میانی تنها پس از اتمام عملیات ترمینال تولید می شود. ارزیابی تنبل با تمام عملیات های میانی در جریان های جاوا کار می کند. استفاده بسیار مفید از ارزیابی تنبل هنگام کار با جریان های بی نهایت رخ می دهد. به این ترتیب از بسیاری از پردازش های غیر ضروری جلوگیری می شود.

خطوط لوله در جاوا استریم

یک خط لوله در یک جریان جاوا شامل یک جریان ورودی، صفر یا چند عملیات میانی است که یکی پس از دیگری ردیف می شوند و در نهایت یک عملیات ترمینال. عملیات میانی در جاوا استریم با تنبلی انجام می شود. این امر، عملیات میانی خط لوله را اجتناب ناپذیر می کند. با خطوط لوله، که اساساً عملیات میانی هستند و به ترتیب ترکیب می شوند، اجرای تنبل ممکن می شود. خطوط لوله کمک می کند تا عملیات میانی را که باید پس از انجام عملیات ترمینال انجام شود، پیگیری می کنند.

نتیجه

اکنون آنچه را که امروز آموخته ایم خلاصه می کنیم. در این مقاله:
  1. ما نگاهی گذرا به جریان های جاوا انداختیم.
  2. سپس تکنیک های مختلفی را برای ایجاد رشته های جاوا در جاوا 8 آموختیم.
  3. ما دو نوع عملیات اصلی (عملیات میانی و عملیات ترمینال) را یاد گرفتیم که می توانند بر روی جریان های جاوا انجام شوند.
  4. سپس به طور مفصل چندین نمونه از عملیات میانی و پایانه را بررسی کردیم.
  5. ما در نهایت به یادگیری بیشتر در مورد ارزیابی تنبل و در نهایت یادگیری در مورد لوله گذاری در موضوعات جاوا رسیدیم.
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION