کلاس Collectors در زبان برنامه نویسی جاوا|Java متدهایی برای عملیات reduction را در خود دارد. از متدهای این کلاس می توان در کار با Stream استفاده کرد. در این مطلب متدهای مهم این کلاس را معرفی می کنیم و نحوه کارکرد آنها را مشخص می کنیم.
این متد نتیجه Stream را در قالب یک لیست می ریزد. نتیجه ای که این متد می دهد یک لیست جدید است و با لیست و مجموعه اصلی کاری ندارد.
List<Integer> integers = Stream.of(1, 2, 3, 4, 5, 6, 6).map(x -> x * x).collect(Collectors.toList());
//output [1, 4, 9, 16, 25, 36, 36]
این متد شبیه به متد toList است با این تفاوت که نتیجه را در یک set ذخیره می کند. و همانطور که می دانید set عنصر تکراری ندارد پس عناصر تکراری نتیجه حذف می شود.
Set<Integer> integers2 = Stream.of(1, 2, 3, 4, 5, 6, 6).map(x -> x * x).collect(Collectors.toSet());
//output [16, 1, 4, 36, 9, 25]
اگر به جز لیست و ست مجموعه دیگری مد نظر شما باشد که نتیجه stream را در آن بریزید می توانید از متد toCollection استفاده کنید. در این متد نوع مجموعه از شما خواسته می شود و داده ها در داخل آن ریخته می شود. مثلا در کد زیر ما نتیجه را در داخل یک LinkedList ریخته ایم.
LinkedList<Integer> integers=Stream.of(1, 2, 3, 4, 5, 6, 6)
.filter(a -> a > 2)
.collect(Collectors.toCollection(LinkedList::new));
//output [3, 4, 5, 6, 6]
این متد تعداد عناصر نتیجه را شمارش می کند و خروجی آن یک عدد long است.
long count=Stream.of(1, 2, 3, 4, 5, 6, 6).filter(s->s>4)
.collect(Collectors.counting());
// output 3
این متد مقدار کمینه یا مینیمم را از استریم برمیگرداند. در کد زیر این کد را برای داده های رشته ای و برای داده های عددی نوشته ایم. اگر می خواهید این متد را برای کلاس های دیگری به کار بگیرید باید فرایند مقایسه را در قسمت comparator پیاده سازی کرده باشید.
int integerMinimum = Stream
.of(1, 2, 3, 4, 5, 6, 6)
.collect(Collectors
.minBy(Comparator.naturalOrder()))
.get();
//output 1
String stringMinimum = Stream
.of("alpha", "beta", "gamma", "tosinso", "mehdi")
.collect(Collectors
.minBy(Comparator.naturalOrder()))
.get();
//output alpha
این متد شبیه به متد minBy است با این تفاوت که مقدار بیشینه یا ماکسیمم را پیدا می کند. مانند کد زیر
int integerMinimum = Stream
.of(1, 2, 3, 4, 5, 6, 6)
.collect(Collectors
.maxBy(Comparator.naturalOrder()))
.get();
//output 6
String stringMinimum = Stream
.of("alpha", "beta", "gamma", "tosinso", "mehdi")
.collect(Collectors
.maxBy(Comparator.naturalOrder()))
.get();
//output tosinso
این متد یک شرط را به عنوان ورودی دریافت می کند و با توجه به شرط مجموعه را به دو لیست تقسیم می کند که یک قسمت را در صورتی که شرط درست نباشد و قسمت دوم در صورتی که شرط درست باشد قرار می دهد. این دو لیست در قالب یک Map برگردانده می شود. پس map به وجود آمده دارای دو عنصر است که کلید اولی مقدار بولین false و دیگری مقدار true است مانند مثال زیر
final Map<Boolean, List<String>> collect = Stream
.of("alpha", "beta", "gamma", "tosinso", "mehdi")
.collect(Collectors
.partitioningBy(s -> s.length() > 4));
//output {false=[beta], true=[alpha, gamma, tosinso, mehdi]}
در این مثال از شرط طول رشته استفاده کرده ایم که مجموعه را به دو قسمت تقسیم کرده ایم یکی اعضایی که طولشان بیشتر از 4 هست و اعضایی که طولشان بیشتر از 4 نیست.
از این متد برای ساخت لیست readOnly استفاده می شود. اگر سعی کنید لیست فقط خواندنی را تغییر بدهید با استثنا UnsupportedOperationException مواجه خواهید شد.
List<String> strings = Stream
.of("alpha", "beta", "gamma", "tosinso", "mehdi")
.collect(Collectors.toUnmodifiableList());
//output [alpha, beta, gamma, tosinso, mehdi]
این متد در جاوای 10 اضافه شده است پس اگر از جاوای 8 یا قبل از آن استفاده می کنید این متد برای شما ناشناخته خواهد بود. همچنین متد toUnmodifiableSet یک set فقط خواندنی را می سازد و شبه به toUnmodifiableList است.
در کلاس Collectors می توان با ترکیب اعضای مجموعه آنها را به رشته تبدیل کرد. در این تبدیل کردن می توان یک جدا کننده بین عناصر قرار داد و یا برای آنها پیشوند و یا پسوند قرار داد. به کد زیر دقت کنید.
List<String> strings = Arrays.asList("alpha","beta","gamma");
String collect3 = strings
.stream()
.distinct()
.collect(Collectors.joining(","));
// output: alpha,beta,gamma
String collect4 = strings
.stream()
.map(s -> s.toString())
.collect(Collectors.joining(",","[","]"));
// output: [alpha,beta,gamma]
در مثال اول علامت کاما(،) را برای جدا کردن اعضا قرار داده ایم. و در مثال دوم براکت را قبل و بعد از اعضا قرار داده ایم.
این متد میانگین مقادیر مجموعه که از نوع long هستند را محاسبه می کند. دقت داشته باشید که مقدار خروجی از نوع double خواهد بود. مانند مثال زیر
List<Long> longValues = Arrays.asList(100l,200l,300l);
Double d1 = longValues
.stream()
.collect(Collectors.averagingLong(x -> x * 2));
// output: 400.0
همچنین متدهای averagingInt و averagingDouble نیز همین کار را برای اعضای int و double انجام می دهد. دقت کنید که خروجی همه این متدها double است.
متد toMap یک map با استفاده از عناصر مجموعه می سازد مانند مثال زیر
List<String> strings = Arrays.asList("alpha","beta","gamma");
Map<String,Integer> map = strings
.stream()
.collect(Collectors
.toMap(Function.identity(),String::length));
// output: {alpha=5, beta=4, gamma=5}
در این مثال یک map ساخته شده است که کلید های آن مقادیر لیست و طول هر رشته به عنوان مقدار آن قرار گرفته شده است.
عناصر تکراری می توانند در یک لیست وجود داشته باشند. حال اگر بخواهیم که مقادیر موجود در لیست را به عنوان کلید در نظر بگیریم مشکل تکراری بودن پیش می آید. در متد toMap می توان این مشکل را به راحتی حل کرد که کلید های یکتا داشته باشیم. مثل کد زیر
List<String> strings = Arrays.asList("alpha","beta","gamma","beta");
Map<String,Integer> map = strings
.stream()
.collect(Collectors
.toMap(Function.identity(),String::length,(i1,i2) -> i1));
// output: {alpha=5, gamma=5, beta=4}
دقت داشته باشید که Function.identity یک اشاره گر است که به عنصر لیست اشاره می کند و i1, i2 دو عنصری هستند که تکراری هستند. در کد بالا به خاطر این که فقط یک مقدار داشته باشیم ما i1 را انتخاب کرده ایم که شما می توانید هرکدام را که مایل بودید انتخاب کنید.
اگر مجموعه شما شامل اعداد صحیح باشد با متد summingInt می توانید مجموع آنها را حساب کنید. مانند مثال زیر
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
Integer sum = integers
.stream()
.collect(Collectors.summingInt(x -> x));
// output: 27
دقت داشته باشید که در کد بالا خود عنصر را به متد summingInt داده ایم ولی می توانید از عملیات مختلفی برای این متد استفاده کنید مانند مثال زیر که مجموعه شامل عناصر رشته ای است ولی ما به متد طول آنها را که int است داده ایم.
List<String> strings = Arrays.asList("alpha","beta","gamma");
Integer collect4 = strings
.stream()
.collect(Collectors.summingInt(String::length));
// output: 18
متدهای summingDouble, summingLong هم مشابه به summingInt هستند با این تفاوت که نوع داده ورودی آنها باید مناسب آنها باشد.
اگر بخواهید در قالب یک شی از کلاس IntSummaryStatistics عملیات ریاضی و آماری اصلی را برای یک مجموعه به دست آورید باید از متد summarizingInt استفاده کنید. این متد مقادیری مانند میانگین و مینیمم و ماکسیمم و تعداد اعضا و جمع آنها را به شما خواهد داد مانند کد زیر
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
IntSummaryStatistics stats = integers
.stream()
.collect(Collectors.summarizingInt(x -> x ));
//output: IntSummaryStatistics{count=7, sum=27, min=1, average=3.857143, max=6}
دقت کنید که با استفاده از کلاس IntSummaryStatistics می توانیم هرکدام از مقادیر را که بخواهیم به شکل زیر دریافت کنیم
stats.getAverage(); // 3.857143
stats.getMax(); // 6
stats.getMin(); // 1
stats.getCount(); // 7
stats.getSum(); // 27
بسیاری از اوقات پیش می آید که می خواهید عناصر را گروه بندی کنید اگر با زبان SQL آشنا باشید متوجه هستید که دستور group by چه کاربردهایی دارد. با استفاده از کلاس Collectors و متد groupingBy می توان عمل گروه بندی را انجام داد. به مثال زیر دقت کنید
List<String> strings = Arrays.asList("alpha","beta","gamma");
Map<Integer, List<String>> collect = strings
.stream()
.collect(Collectors.groupingBy(String::length));
// output: {4=[beta], 5=[alpha, gamma]}
در مجموعه بالا عناصر را بر اساس طول رشته گروه بندی کرده ایم که خروجی یک map است که کلید آن طول رشته و مقدار آن یک لیست از رشته هایی است که طولشان برابر با عدد کلید است. حال شما می توانید به جای طول رشته منطق مورد نظر خود را برای گروه بندی پیاده سازی کنید. همچنین می توانیم نوع خروجی مجموعه را به شکل زیر تعیین کنیم که در کد زیر خروجی به شکل یک LinkedList آورده شده است.
List<String> strings = Arrays.asList("alpha","beta","gamma");
Map<Integer, LinkedList<String>> collect1 = strings
.stream()
.collect(Collectors.groupingBy(String::length,
Collectors.toCollection(LinkedList::new)));
// output: {4=[beta], 5=[alpha, gamma]}
بنیانگذار توسینسو و برنامه نویس
مهدی عادلی، بنیان گذار TOSINSO. کارشناس ارشد نرم افزار کامپیوتر از دانشگاه صنعتی امیرکبیر و #C و جاوا و اندروید کار می کنم. در زمینه های موبایل و وب و ویندوز فعالیت دارم و به طراحی نرم افزار و اصول مهندسی نرم افزار علاقه مندم.
زمان پاسخ گویی روز های شنبه الی چهارشنبه ساعت 9 الی 18
فقط به موضوعات مربوط به محصولات آموزشی و فروش پاسخ داده می شود