آیا باید بدانید که چگونه اشیاء جاوا را در یک مجموعه، آرایه یا نقشه مرتب کنید؟ در اینجا نحوه استفاده از رابط های Comparable و Comparator و اجتناب از ClassCastException ها آورده شده است.
برنامه نویسان اغلب نیاز دارند عناصر را از پایگاه داده به مجموعه، آرایه یا نقشه مرتب کنند. در جاوا، میتوانیم هر الگوریتم مرتبسازی را با هر نوع که بخواهیم پیادهسازی کنیم.
با استفاده از رابط Comparable
و روش compareTo()
، میتوانیم با استفاده از ترتیب حروف الفبا، طول String
، ترتیب حروف الفبا معکوس یا اعداد مرتب کنیم. . رابط Comparator
به ما امکان می دهد همین کار را انجام دهیم اما به روشی انعطاف پذیرتر.
هر کاری که میخواهیم انجام دهیم، فقط باید بدانیم که چگونه منطق مرتبسازی صحیح را برای رابط و نوع دادهشده پیادهسازی کنیم.
مرتبسازی با جاوا Comparable و Comparator
در اینجا چیزی است که در این مقاله درباره مرتب سازی اشیاء جاوا خواهید آموخت:
- مرتبسازی با
مقایسه
:- مرتب سازی
لیست
- نحوه عملکرد
compareTo()
جاوا - نحوه مرتب سازی آرایه ها در جاوا
- مرتبسازی جاوا
نقشه
باTreeMap
- مرتب کردن
Set
جاوا باTreeSet
- نحوه اجتناب از
ClassCastException
هنگام مرتبسازی - استفاده از
مقایسه
با کلاسهای اصلی جاوا
جاوا
- مرتب سازی
- مرتب سازی
لیست
- نحوه عملکرد
compareTo()
جاوا - نحوه مرتب سازی آرایه ها در جاوا
- مرتبسازی جاوا
نقشه
باTreeMap
- مرتب کردن
Set
جاوا باTreeSet
- نحوه اجتناب از
ClassCastException
هنگام مرتبسازی - استفاده از
مقایسه
با کلاسهای اصلی جاوا
جاوا
- مرتب سازی با
مقایسه کننده
:- استفاده از
مقایسه کننده
با کلاس های داخلی ناشناس - استفاده از
مقایسه کننده
با عبارات لامبدا
- استفاده از
- استفاده از
مقایسه کننده
با کلاس های داخلی ناشناس - استفاده از
مقایسه کننده
با عبارات لامبدا
مرتبسازی با Comparable
ما با نحوه مرتبسازی با استفاده از مقایسه. ما زمانی از مقایسهپذیر
استفاده میکنیم که یک مقایسه پیشفرض برای شی مورد نظر وجود داشته باشد.
مرتب سازی فهرست جاوا
در این مثال اول، Comparable
را در کلاس Simpson
با استفاده از Simpson
در نوع عمومی پیادهسازی میکنیم:
class Simpson implements Comparable<Simpson> {
String name;
Simpson(String name) {
this.name = name;
}
@Override
public int compareTo(Simpson simpson) {
return this.name.compareTo(simpson.name);
}
}
public class SimpsonSorting {
public static void main(String... sortingWithList) {
List<SimpsonCharacter> simpsons = new ArrayList<>();
simpsons.add(new SimpsonCharacter("Homer "));
simpsons.add(new SimpsonCharacter("Marge "));
simpsons.add(new SimpsonCharacter("Bart "));
simpsons.add(new SimpsonCharacter("Lisa "));
Collections.sort(simpsons);
simpsons.stream().map(s -> s.name).forEach(System.out::print);
Collections.reverse(simpsons);
simpsons.stream().forEach(System.out::print);
}
}
توجه داشته باشید که compareTo() و در یک شی Simpson
دیگر ارسال شد. ما همچنین روش toString()
را لغو کردهایم، فقط برای آسانتر خواندن مثال.
چاپ با toString
روش toString
تمام اطلاعات شی را نشان می دهد. وقتی شی را چاپ می کنیم، خروجی همان چیزی است که در toString()
پیاده سازی شده است.
جاوا compareTo() چگونه کار می کند
روش compareTo()
یک شی داده شده یا نمونه فعلی را با یک شی مشخص شده مقایسه می کند تا ترتیب اشیا را تعیین کند. در اینجا نگاهی گذرا به نحوه عملکرد compareTo()
داریم.
روش compareTo()
اگر مقایسه برگردد … |
سپس… |
|
|
|
|
|
|
اگر مقایسه برگردد …
سپس…
>= ۱
this.name > simpson.name
۰
this.name == simpson.name
<= -1
this.name < simpson.name
ما فقط می توانیم از کلاس هایی استفاده کنیم که با متد sort()
قابل مقایسه باشند. اگر سعی کنیم Simpson
را ارسال کنیم که مقایسه
را اجرا نمی کند، یک خطای کامپایل دریافت خواهیم کرد.
روش sort()
از استفاده می کند چندشکلی با عبور از هر شیئی که مقایسه
است. سپس اشیاء طبق انتظار مرتب خواهند شد.
خروجی کد قبلی این خواهد بود:
Bart Homer Lisa Marge
اگر میخواهیم ترتیب را معکوس کنیم، میتوانیم sort()
را با reverse()
مبادله کنیم. از:
Collections.sort(simpsons);
به:
Collections.reverse(simpsons);
استقرار روش reverse()
خروجی قبلی را به:
تغییر میدهد
Marge Lisa Homer Bart
نحوه مرتب سازی آرایه جاوا
در جاوا، ما میتوانیم آرایهای را با هر نوع که میخواهیم مرتب کنیم تا زمانی که رابط Comparable
را پیادهسازی کند. این یک مثال است:
public class ArraySorting {
public static void main(String... moeTavern) {
int[] moesPints = new int[] {9, 8, 7, 6, 1};
Arrays.sort(moesPints);
Arrays.stream(moesPints).forEach(System.out::print);
Simpson[] simpsons = new Simpson[]{new Simpson("Lisa"), new Simpson("Homer")};
Arrays.sort(simpsons);
Arrays.stream(simpsons).forEach(System.out::println);
}
}
در اولین فراخوانی sort()
، آرایه به ترتیب زیر طبقه بندی می شود:
۱ ۶ ۷ ۸ ۹
در دومین فراخوان sort()
، به این صورت مرتب شده است:
Homer Lisa
بهخاطر داشته باشید که اشیاء سفارشی باید مقایسهپذیر
را پیادهسازی کنند تا حتی بهعنوان یک آرایه مرتب شوند.
اجتناب از ClassCastExceptions هنگام مرتب کردن اشیاء جاوا
اگر شی سیمپسون مقایسه
را اجرا نمی کرد، یک ClassCastException پرتاب می شود. اگر این را به عنوان آزمایش اجرا کنید، چیزی شبیه خروجی زیر خواهید دید:
Error:(16, 20) java: no suitable method found for sort(java.util.List<com.javaworld.javachallengers.sortingcomparable.Simpson>)
method java.util.Collections.<T>sort(java.util.List<T>) is not applicable
(inference variable T has incompatible bounds
equality constraints: com.javaworld.javachallengers.sortingcomparable.Simpson
lower bounds: java.lang.Comparable<? super T>)
method java.util.Collections.<T>sort(java.util.List<T>,java.util.Comparator<? super T>) is not applicable
(cannot infer type-variable(s) T
(actual and formal argument lists differ in length))
این گزارش ممکن است گیج کننده باشد، اما نگران نباشید. فقط به خاطر داشته باشید که یک ClassCastException
برای هر شی مرتب شده ای که رابط مقایسه
را اجرا نمی کند پرتاب می شود.
مرتب سازی نقشه با TreeMap
جاوا API شامل کلاس های زیادی برای کمک به مرتبسازی، از جمله TreeMap< /a>. در مثال زیر، ما از TreeMap
برای مرتب کردن کلیدها در Map
استفاده میکنیم.
public class TreeMapExample {
public static void main(String... barney) {
Map<SimpsonCharacter, String> simpsonsCharacters = new TreeMap<>();
simpsonsCharacters.put(new SimpsonCharacter("Moe"), "shotgun");
simpsonsCharacters.put(new SimpsonCharacter("Lenny"), "Carl");
simpsonsCharacters.put(new SimpsonCharacter("Homer"), "television");
simpsonsCharacters.put(new SimpsonCharacter("Barney"), "beer");
System.out.println(simpsonsCharacters);
}
}
TreeMap
از روش compareTo()
استفاده می کند که توسط رابط Comparable
پیاده سازی شده است. هر عنصر در Map
حاصل بر اساس کلید خود مرتب شده است. در این حالت، خروجی این خواهد بود:
Barney=beer, Homer=television, Lenny=Carl, Moe=shotgun
به یاد داشته باشید: اگر شیء مقایسه
را پیاده سازی نکند، یک ClassCastException
دریافت خواهید کرد.
مرتب کردن مجموعه با TreeSet
واسط Set
مسئول ذخیره مقادیر منحصر به فرد است، اما زمانی که از TreeSet پیاده سازی، عناصر درج شده به طور خودکار با اضافه کردن آنها مرتب می شوند:
public class TreeSetExample {
public static void main(String... barney) {
Set<SimpsonCharacter> simpsonsCharacters = new TreeSet<>();
simpsonsCharacters.add(new SimpsonCharacter("Moe"));
simpsonsCharacters.add(new SimpsonCharacter("Lenny"));
simpsonsCharacters.add(new SimpsonCharacter("Homer"));
simpsonsCharacters.add(new SimpsonCharacter("Barney"));
System.out.println(simpsonsCharacters);
}
}
خروجی این کد این است:
Barney, Homer, Lenny, Moe
دوباره، اگر از شیئی استفاده کنیم که قابل مقایسه
نیست، یک ClassCastException
دریافت خواهیم کرد.
در مرحله بعدی: استفاده از Comparable با کلاس های اصلی جاوا
استفاده از Comparable با کلاس های اصلی جاوا
بسیاری از کلاسها و اشیاء اصلی جاوا رابط Comparable
را پیادهسازی میکنند، به این معنی که ما مجبور نیستیم منطق compareTo()
را برای آن کلاسها پیادهسازی کنیم. در اینجا چند مثال آشنا آورده شده است:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence { ...
public final class Integer extends Number implements Comparable<Integer> { …
public final class Double extends Number implements Comparable<Double> {...
مرتب سازی با مقایسه
اگر نخواهیم از همان روش compareTo()
از کلاس POJO استفاده کنیم، چه؟ آیا میتوانیم روش مقایسه
را برای استفاده از منطقی متفاوت لغو کنیم؟ در زیر یک مثال آمده است:
public class BadExampleOfComparable {
public static void main(String... args) {
List<SimpsonCharacter> characters = new ArrayList<>();
SimpsonCharacter homer = new SimpsonCharacter("Homer") {
@Override
public int compareTo(SimpsonCharacter simpson) {
return this.name.length() - (simpson.name.length());
}
};
SimpsonCharacter moe = new SimpsonCharacter("Moe") {
@Override
public int compareTo(SimpsonCharacter simpson) {
return this.name.length() - (simpson.name.length());
}
};
characters.add(homer);
characters.add(moe);
Collections.sort(characters);
System.out.println(characters);
}
}
همانطور که می بینید، این کد پیچیده است و شامل تکرارهای زیادی است. ما مجبور شدیم برای یک منطق دو بار روش compareTo()
را لغو کنیم. اگر عناصر بیشتری وجود داشت، باید منطق را برای هر شیء تکرار کنیم.
خوشبختانه، ما Comparator، که به ما امکان می دهد منطق compareTo()
را از کلاس های جاوا جدا کنیم. همان مثال بالا را که با استفاده از Comparator
:
بازنویسی شده است، در نظر بگیرید
public class GoodExampleOfComparator {
public static void main(String... args) {
List<SimpsonCharacter> characters = new ArrayList<>();
SimpsonCharacter homer = new SimpsonCharacter("Homer");
SimpsonCharacter moe = new SimpsonCharacter("Moe");
characters.add(homer);
characters.add(moe);
Collections.sort(characters, (Comparator.<SimpsonCharacter>
comparingInt(character1 -> character1.name.length())
.thenComparingInt(character2 -> character2.name.length())));
System.out.println(characters);
}
}
تفاوت های مقایسه کننده و مقایسه کننده
این مثال ها تفاوت اصلی بین مقایسه
و مقایسه
را نشان می دهد. هنگامی که یک مقایسه پیش فرض برای شی شما وجود دارد، از مقایسه
استفاده کنید. زمانی که باید در مورد compareTo()
موجود کار کنید، یا زمانی که نیاز به استفاده از منطق خاص به روشی انعطافپذیرتر دارید، از Comparator
استفاده کنید. مقایسه کننده
منطق مرتب سازی را از شی شما جدا می کند و منطق compareTo()
را در روش sort()
شما دارد.
استفاده از مقایسه کننده با کلاس های داخلی ناشناس
در این مثال بعدی، ما از یک ناشناس استفاده می کنیم کلاس داخلی برای مقایسه ارزش اشیا. یک کلاس داخلی ناشناس، در این مورد، هر کلاسی است که Comparator
را پیادهسازی میکند. استفاده از آن به این معنی است که ما ملزم به نمونه سازی کلاس نامگذاری شده در پیاده سازی یک رابط نیستیم. در عوض، ما متد compareTo()
را در داخل کلاس داخلی ناشناس پیادهسازی میکنیم.
public class MarvelComparator {
public static void main(String... comparator) {
List<String> marvelHeroes = new ArrayList<>();
marvelHeroes.add("SpiderMan ");
marvelHeroes.add("Wolverine ");
marvelHeroes.add("Xavier ");
marvelHeroes.add("Cyclops ");
Collections.sort(marvelHeroes, new Comparator<String>() {
@Override
public int compare(String hero1, String hero2) {
return hero1.compareTo(hero2);
}
});
Collections.sort(marvelHeroes, (m1, m2) -> m1.compareTo(m2));
Collections.sort(marvelHeroes, Comparator.naturalOrder());
marvelHeroes.forEach(System.out::print);
}
}
اطلاعات بیشتر در مورد کلاس های داخلی
یک کلاس داخلی ناشناس به سادگی هر کلاسی است که نام آن مهم نیست و رابطی را که ما اعلام می کنیم پیاده سازی می کند. بنابراین، در مثال، Comparator
جدید در واقع نمونهسازی کلاسی است که نامی ندارد، که متد را با منطقی که ما میخواهیم پیادهسازی میکند.
استفاده از مقایسه کننده با عبارات لامبدا
کلاسهای داخلی ناشناس پرحرف هستند، که میتواند در کد ما مشکل ایجاد کند. در رابط Comparator
، میتوانیم از عبارات لامبدا برای سادهسازی و خوانایی کد استفاده کنیم. برای مثال، میتوانیم این را تغییر دهیم:
Collections.sort(marvel, new Comparator<String>() {
@Override
public int compare(String hero1, String hero2) {
return hero1.compareTo(hero2);
}
});
به این:
Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2));
کد کمتر و نتیجه یکسان!
خروجی این کد خواهد بود:
Cyclops SpiderMan Wolverine Xavier
میتوانیم با تغییر این کد را سادهتر کنیم:
Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2));
به این:
Collections.sort(marvel, Comparator.naturalOrder());
عبارات لامبدا در جاوا
درباره عبارات لامبدا در جاوا بیشتر بیاموزید.
چالش رابط Comparable را انجام دهید!
آنچه را که آموختهاید با مشخص کردن خروجی کد زیر آزمایش کنید. به یاد داشته باشید، اگر این چالش را فقط با مطالعه آن برای خودتان حل کنید، بهتر یاد خواهید گرفت. پس از رسیدن به پاسخ، می توانید پاسخ زیر را بررسی کنید. همچنین می توانید تست های خود را برای جذب کامل مفاهیم اجرا کنید.
public class SortComparableChallenge {
public static void main(String... doYourBest) {
Set<Simpson> set = new TreeSet<>();
set.add(new Simpson("Homer"));
set.add(new Simpson("Marge"));
set.add(new Simpson("Lisa"));
set.add(new Simpson("Bart"));
set.add(new Simpson("Maggie"));
List<Simpson> list = new ArrayList<>();
list.addAll(set);
Collections.reverse(list);
list.forEach(System.out::println);
}
static class Simpson implements Comparable<Simpson> {
String name;
public Simpson(String name) {
this.name = name;
}
public int compareTo(Simpson simpson) {
return simpson.name.compareTo(this.name);
}
public String toString() {
return this.name;
}
}
}
A)
Bart
Homer
Lisa
Maggie
Marge
B)
Maggie
Bart
Lisa
Marge
Homer
ج)
Marge
Maggie
Lisa
Homer
Bart
D)
Indeterminate
TreeSet and reverse()
با نگاهی به کد، اولین چیزی که باید متوجه شوید این است که ما از TreeSet
استفاده می کنیم، بنابراین عناصر به طور خودکار مرتب می شوند. دومین مورد این است که ترتیب مقایسه معکوس است، بنابراین مرتب سازی به ترتیب معکوس انجام می شود.
وقتی برای اولین بار عناصر را به لیست اضافه می کنیم، TreeSet
به طور خودکار آنها را به این صورت مرتب می کند:
Marge
Maggie
Lisa
Homer
Bart
سپس از روش reverse()
استفاده می کنیم که ترتیب عناصر را معکوس می کند. بنابراین خروجی نهایی A):
خواهد بود
Bart
Homer
Lisa
Maggie
Marge
چالش ویدیویی! اشکال زدایی sort() و TreeSet
اشکالزدایی یکی از سادهترین راهها برای جذب کامل مفاهیم برنامهنویسی و در عین حال بهبود کد شماست. در این ویدیو میتوانید در حین رفع اشکال و توضیح چالش مرتبسازی جاوا همراه باشید:
اشتباهات رایج با Comparable
- تلاش برای مرتب کردن یک شی غیر قابل مقایسه در روش
sort()
. - استفاده از
مقایسه
برای استراتژیهای مرتبسازی مختلف در یک شی. - مقایسه معکوس در روش
compareTo()
به طوری که مرتبسازی به ترتیب معکوس باشد، مانند:
ترتیب عادی:public int compareTo(Simpson simpson) { this.name.compareTo(simpson.name); }
ترتیب معکوس:
public int compareTo(Simpson simpson) { simpson.name.compareTo(this.name); }
- افزودن یک شی غیر قابل مقایسه (هر شیئی که
مقایسه
را اجرا نمی کند) در یک شیTreeSet
یاTreeMap
.
public int compareTo(Simpson simpson) {
this.name.compareTo(simpson.name);
}
public int compareTo(Simpson simpson) {
simpson.name.compareTo(this.name);
}
چه چیزی را در مورد مرتب سازی با جاوا به خاطر بسپارید
- از
مقایسه
هنگامی که مقایسه برای کلاس داده شده استاندارد است استفاده کنید. - از
مقایسه کننده
هنگامی که به انعطاف پذیری بیشتری نیاز دارید استفاده کنید. - امکان استفاده از لامبدا با
مقایسه کننده
وجود دارد. - بسیاری از کلاس های اصلی جاوا
مقایسه
را پیاده سازی می کنند. - از
TreeMap
یاTreeSet
هنگام مرتبسازیMap
یاSet
استفاده کنید. - روش
compareTo()
باComparable
وComparator
کار میکند. - متد
compareTo()
اگر یک شی بزرگتر از دیگری باشد یک عدد مثبت، اگر کوچکتر باشد منفی و اگر یکسان باشند صفر را برمیگرداند.
پست های مرتبط
مرتب سازی اشیاء جاوا با Comparable و Comparator
مرتب سازی اشیاء جاوا با Comparable و Comparator
مرتب سازی اشیاء جاوا با Comparable و Comparator