به لطف تعریف Functional Interfaceها، Interface خوب قدیمی Comparator<T> که در JDK 2 معرفی شد، Functional شد. بنابراین پیادهسازی Comparator میتواند با Lambda Expression انجام شود.
تنها متد abstract Interface Comparator<T>:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
قرارداد Comparator به این صورت است:
o1 < o2 باشد، compare(o1, o2) باید عدد منفی برگرداندo1 > o2 باشد، compare(o1, o2) باید عدد مثبت برگرداندcompare(o1, o2) و compare(o2, o1) باید علامتهای مخالف داشته باشند.لازم نیست بهطور سختگیرانه وقتی o1.equals(o2) برابر true است، مقایسهی o1 و o2 عدد ۰ برگرداند.
چگونه Comparatorای از اعداد صحیح بسازید که ترتیب طبیعی را پیادهسازی کند؟ متدی که در ابتدای این آموزش دیدید را استفاده کنید:
Comparator<Integer> comparator = (i1, i2) -> Integer.compare(i1, i2);
ممکن است متوجه شده باشید این Lambda Expression را میتوان با Static Method Reference زیبا نوشت:
Comparator<Integer> comparator = Integer::compare;
از پیادهسازی این Comparator با
(i1 - i2)اجتناب کنید. حتی اگر این الگو کار به نظر برسد، موارد خاصی وجود دارد که نتیجهی صحیح تولید نمیکند. برای اطلاعات بیشتر کد منبع متدInteger.compare(i1, i2)را ببینید.
API Comparator یک قدم جلوتر رفته و API مفیدی برای ایجاد Comparatorها به شکل خواناتر ارائه داده.
فرض کنید نیاز به Comparatorای برای مقایسهی رشتهها به شکل غیرطبیعی دارید: رشتههای کوتاهتر کوچکتر از رشتههای بلندتر هستند:
Comparator<String> comparator =
(s1, s2) -> Integer.compare(s1.length(), s2.length());
این کد را میتوانید به شکل دیگری بازنویسی کنید:
Function<String, Integer> toLength = String::length;
Comparator<String> comparator =
(s1, s2) -> Integer.compare(
toLength.apply(s1),
toLength.apply(s2));
اکنون میبینید کد این Comparator فقط به Function به نام toLength وابسته است. پس ایجاد Factory Methodای که این Function را به عنوان آرگومان بگیرد و Comparator<String> متناظر برگرداند، ممکن میشود.
Factory Method مورد نظر در JDK وجود دارد و به Interface Comparator اضافه شده:
Comparator<String> comparator = Comparator.comparing(String::length);
متد comparing() Static Method Interface Comparator است. Functionای میگیرد که نوع بازگشتیاش امتدادی از Comparable باشد.
فرض کنید کلاس User با getter getName() دارید و نیاز به مرتبسازی لیستی از کاربران بر اساس نام دارید:
List<User> users = ...;
Comparator<User> byName = Comparator.comparing(User::getName);
users.sort(byName);
---
شرکت شما از Comparable<User> تحویلدادهشده بسیار راضی است. اما الزام جدیدی در نسخهی ۲ وجود دارد: کلاس User اکنون firstName و lastName دارد.
Comparator<User> byLastName = Comparator.comparing(User::getLastName);
Comparator<User> byFirstName = Comparator.comparing(User::getFirstName);
اکنون نیاز به راهی برای زنجیرهای کردن آنها دارید:
Comparator<User> byLastNameThenFirstName =
byLastName.thenComparing(byFirstName);
متد thenComparing() Default Method Interface Comparator است.
الگوی سادهتر:
Comparator<User> byLastNameThenFirstName =
Comparator.comparing(User::getLastName)
.thenComparing(User::getFirstName);
با Lambda Expression، Method Reference، زنجیرهای کردن و ترکیب، ایجاد Comparatorها هرگز به این آسانی نبوده!
---Boxing و Unboxing انواع اولیه میتواند در Comparatorها هم رخ دهد. برای مدیریت این مشکل، نسخههای تخصصی متدهای comparing() و thenComparing() اضافه شده:
comparingInt(ToIntFunction<T> keyExtractor)comparingLong(ToLongFunction<T> keyExtractor)comparingDouble(ToDoubleFunction<T> keyExtractor)متدهای معادل زنجیرهای هم وجود دارد: thenComparingInt()، thenComparingLong() و thenComparingDouble().
چند Factory Method ارزشمند در این آموزش وجود دارد. کلاسهای زیادی در JDK Interface خاصی را پیادهسازی میکنند: Interface Comparable<T>. این Interface متدی دارد: compareTo(T other) که int برمیگرداند.
میتوانید نمونههای این کلاسها را با ترتیب طبیعیشان مقایسه کنید. Factory Method Comparator.naturalOrder() دقیقاً همین کار را انجام میدهد.
مثالی که رشتهها را ابتدا بر اساس طول و سپس ترتیب طبیعی مقایسه میکند (این مثال از Static Import برای naturalOrder() استفاده میکند):
Comparator<String> byLengthThenAlphabetically =
Comparator.comparing(String::length)
.thenComparing(naturalOrder());
List<String> strings = Arrays.asList("one", "two", "three", "four", "five");
strings.sort(byLengthThenAlphabetically);
IO.println(strings);
خروجی:
[one, two, five, four, three]
---
یکی از استفادههای اصلی Comparatorها مرتبسازی لیستهایی از اشیا است. JDK 8 متد List.sort() را به Interface List اضافه کرد.
اگر نیاز به مرتبسازی لیست قبلی به ترتیب معکوس دارید، از متد reversed() استفاده کنید:
List<String> strings =
Arrays.asList("one", "two", "three", "four", "five");
strings.sort(byLengthThenAlphabetically.reversed());
IO.println(strings);
خروجی:
[three, four, five, two, one]
---
مقایسهی اشیای null میتواند به NullPointerException منجر شود.
فرض کنید نیاز به Comparator null-safe اعداد صحیح دارید. قرارداد: مقادیر null در انتهای لیست قرار بگیرند و مقادیر non-null به ترتیب طبیعی مرتب شوند:
Comparator<Integer> comparator =
(i1, i2) -> {
if (i1 == null && i2 != null) {
return 1;
} else if (i1 != null && i2 == null) {
return -1;
} else {
return Integer.compare(i1, i2);
}
};
راه بسیار آسانتری با Factory Method دیگری از Interface Comparator وجود دارد:
Comparator<Integer> naturalOrder = Comparator.naturalOrder();
Comparator<Integer> naturalOrderNullsLast =
Comparator.nullsLast(naturalOrder());
متد nullsLast() و خواهرش nullsFirst() Factory Methodهای Interface Comparator هستند. هر دو Comparator میگیرند و مقادیر null را مدیریت میکنند.
مثال:
List<String> strings =
Arrays.asList("one", null, "two", "three", null, null, "four", "five");
Comparator<String> naturalNullsLast =
Comparator.nullsLast(naturalOrder());
strings.sort(naturalNullsLast);
IO.println(strings);
خروجی:
[five, four, one, three, two, null, null, null]
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/