JavaRush /جاوا بلاگ /Random-UR /جاوا میں موازنہ کرنے والا
Viacheslav
سطح

جاوا میں موازنہ کرنے والا

گروپ میں شائع ہوا۔
جاوا میں موازنہ اور موازنہ کے بارے میں صرف سست لوگوں نے نہیں لکھا ہے۔ میں سست نہیں ہوں - لہذا میں آپ سے ایک اور تغیر سے محبت کرنے اور اس کی حمایت کرنے کو کہتا ہوں۔ مجھے امید ہے کہ یہ ضرورت سے زیادہ نہیں ہوگا۔ اور ہاں، یہ مضمون اس سوال کا جواب ہے: "کیا آپ میموری سے موازنہ لکھ سکتے ہیں؟" مجھے امید ہے کہ اس مضمون کو پڑھنے کے بعد ہر کوئی یادداشت سے موازنہ لکھنے کے قابل ہو جائے گا۔
جاوا میں موازنہ کرنے والا - 1
تعارف جاوا ایک آبجیکٹ پر مبنی زبان کے طور پر جانا جاتا ہے۔ نتیجے کے طور پر، جاوا میں اشیاء کے ساتھ کام کرنا عام ہے۔ لیکن جلد یا بدیر کسی نہ کسی اصول کے مطابق اشیاء کا موازنہ کرنے کا کام ابھرتا ہے۔ تو، دیا گیا: ہمارے پاس کچھ پیغام ہے، جسے Message کلاس نے بیان کیا ہے:
public static class Message {
    private String message;
    private int id;

    public Message(String message) {
        this.message = message;
        this.id = new Random().nextInt(1000);
    }
    public String getMessage() {
        return message;
    }
    public Integer getId() {
        return id;
    }
    public String toString() {
        return "[" + id + "] " + message;
    }
}
آئیے اس کلاس کو Tutorialspoint java compiler میں شامل کریں ۔ آئیے درآمدات کو شامل کرنا بھی یاد رکھیں:
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
مرکزی طریقہ میں ہم کئی پیغامات بنائیں گے:
public static void main(String[] args){
    List<Message> messages = new ArrayList();
    messages.add(new Message("Hello, World!"));
    messages.add(new Message("Hello, Sun!"));
    System.out.println(messages);
}
آئیے سوچتے ہیں کہ اگر ہم ان کا موازنہ کرنا چاہتے ہیں تو ہمیں کیا کرنا چاہیے؟ مثال کے طور پر، ہم شناخت کے لحاظ سے ترتیب دینا چاہتے ہیں۔ اور ترتیب پیدا کرنے کے لیے، آپ کو کسی نہ کسی طرح اشیاء کا موازنہ کرنے کی ضرورت ہے تاکہ یہ سمجھ سکیں کہ کون سی چیز پچھلی ہے (یعنی چھوٹی) اور کون سی اگلی ہے (یعنی بڑی)۔ آئیے java.lang.Object جیسی کلاس سے شروعات کریں ۔ جیسا کہ ہم جانتے ہیں، تمام کلاس اس آبجیکٹ کلاس سے وراثت میں ملتی ہیں۔ اور یہ منطقی ہے، کیونکہ یہ بنیادی طور پر اس تصور کا اظہار کرتا ہے: "ہر چیز ایک چیز ہے" اور تمام طبقات کے لیے مشترکہ رویہ فراہم کرتی ہے۔ اور یہ کلاس اس بات کی وضاحت کرتی ہے کہ ہر کلاس کے دو طریقے ہیں: → hashCode hashCode طریقہ کلاس کی مثال کے طور پر آبجیکٹ کی کچھ عددی (int) نمائندگی کرتا ہے۔ اس کا کیا مطلب ہے؟ اس کا مطلب یہ ہے کہ اگر آپ نے کلاس کی دو مختلف مثالیں بنائی ہیں، تو چونکہ مثالیں مختلف ہیں، اس لیے ان کا ہیش کوڈ مختلف ہونا چاہیے۔ طریقہ کار کی تفصیل میں یہ وہی کہتا ہے: "جتنا معقول حد تک عملی ہے، کلاس آبجیکٹ کے ذریعہ بیان کردہ ہیش کوڈ کا طریقہ الگ الگ اشیاء کے لیے الگ الگ عدد واپس کرتا ہے" یعنی، اگر یہ دو مختلف مثالیں ہیں، تو ان میں مختلف ہونا چاہیے۔ hashCodes. یعنی یہ طریقہ ہمارے مقابلے کے لیے موزوں نہیں ہے۔ → equals مساوی طریقہ سوال کا جواب دیتا ہے "آجیکٹ برابر ہیں" اور ایک بولین لوٹاتا ہے۔ اس طریقہ کا ڈیفالٹ کوڈ ہے:
public boolean equals(Object obj) {
    return (this == obj);
}
یعنی کسی شے پر اس طریقہ کو زیر کیے بغیر، یہ طریقہ بنیادی طور پر یہ بتاتا ہے کہ آیا آبجیکٹ کے حوالہ جات مماثل ہیں یا نہیں۔ یہ ہمارے پیغامات کے لیے موزوں نہیں ہے، کیونکہ ہمیں اعتراض کے لنکس میں دلچسپی نہیں ہے، ہمیں میسج آئی ڈی میں دلچسپی ہے۔ اور یہاں تک کہ اگر ہم مساوی طریقہ کو اوور رائیڈ کرتے ہیں، تو زیادہ سے زیادہ ہمیں ملے گا: "وہ برابر ہیں" یا "وہ برابر نہیں ہیں۔" لیکن یہ ہمارے لیے ترتیب کا تعین کرنے کے لیے کافی نہیں ہے۔

جاوا میں موازنہ اور موازنہ

ہمیں کیا سوٹ؟ اگر ہم مترجم میں لفظ "موازنہ" کا انگریزی میں ترجمہ کرتے ہیں، تو ہمیں ترجمہ "موازنہ" ملے گا۔ بہت اچھا، پھر ہمیں کسی ایسے شخص کی ضرورت ہے جو موازنہ کرے۔ اگر آپ اس موازنہ کا موازنہ کریں تو جو موازنہ کرتا ہے وہ موازنہ کرنے والا ہے۔ آئیے Java Api کھولیں اور وہاں Comparator تلاش کریں ۔ اور درحقیقت، ایسا ایک انٹرفیس ہے - java.util.Comparator java.util.Comparator اور java.lang.Comparable جیسا کہ آپ دیکھ سکتے ہیں، ایسا ایک انٹرفیس ہے۔ اس کو نافذ کرنے والی کلاس کہتی ہے کہ "میں اشیاء کا موازنہ کرنے کے لیے ایک فنکشن نافذ کر رہا ہوں۔" واقعی یاد رکھنے والی واحد چیز موازنہ کنٹریکٹ ہے، جس کا اظہار اس طرح کیا گیا ہے:

Comparator возвращает int по следующей схеме: 
  • отрицательный int (первый an object отрицательный, то есть меньше)
  • положительный int (первый an object положительный, хороший, то есть больший)
  • ноль = an objectы равны
اب ایک کمپیریٹر لکھتے ہیں۔ ہمیں java.util.Comparator درآمد کرنے کی ضرورت ہوگی ۔ درآمد کرنے کے بعد، مین میں ایک طریقہ شامل کریں: Comparator<Message> comparator = new Comparator<Message>(); قدرتی طور پر، یہ کام نہیں کرے گا، کیونکہ کمپیریٹر ایک انٹرفیس ہے۔ لہذا، قوسین کے بعد ہم گھوبگھرالی کو شامل کریں گے { }۔ ان بریکٹ میں ہم طریقہ لکھیں گے:
public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
یہ لکھنے کے لیے آپ کو یاد کرنے کی بھی ضرورت نہیں ہے۔ موازنہ کرنے والا وہ ہوتا ہے جو موازنہ کرتا ہے، یعنی موازنہ کرتا ہے۔ اس سوال کا جواب دینے کے لیے کہ مقابلے کی اشیاء کس ترتیب میں ہیں، ہم int واپس کرتے ہیں۔ یہ سب ہے، اصل میں. سادہ اور آسانی سے۔ جیسا کہ ہم مثال سے دیکھ سکتے ہیں، Comparator کے علاوہ، ایک اور انٹرفیس ہے - java.lang.Comparable ، جس پر عمل درآمد کرتے ہوئے ہمیں compareTo طریقہ کی وضاحت کرنی چاہیے ۔ یہ انٹرفیس کہتا ہے کہ "ایک کلاس جو انٹرفیس کو لاگو کرتی ہے وہ کلاس کی مثالوں کا موازنہ کرنے کی اجازت دیتی ہے۔" مثال کے طور پر، CompareTo کا Integer کا نفاذ اس طرح لگتا ہے:
(x < y) ? -1 : ((x == y) ? 0 : 1)
ان تمام انٹرفیس کو کیسے یاد رکھیں؟ کس کے لئے؟ سب کچھ انگریزی سے آتا ہے۔ موازنہ کرنا - موازنہ کرنے کے لیے، موازنہ کرنے والا Comparator ہے (بطور رجسٹرار، مثال کے طور پر۔ یعنی وہ جو رجسٹر کرتا ہے)، اور صفت "موازنہ" Comparable ہے۔ ٹھیک ہے، "موازنہ کے ساتھ" کا ترجمہ نہ صرف موازنہ کے طور پر کیا جاتا ہے بلکہ موازنہ کے طور پر بھی کیا جاتا ہے۔ یہ آسان ہے. جاوا زبان انگریزی بولنے والے لوگوں کے ذریعہ لکھی گئی تھی، اور جاوا میں ہر چیز کا نام دینے میں وہ صرف انگریزی سے رہنمائی کرتے تھے اور نام دینے میں ایک قسم کی منطق تھی۔ اور compareTo طریقہ بیان کرتا ہے کہ کلاس کی مثال کا دیگر مثالوں سے موازنہ کیسے کیا جانا چاہیے۔ مثال کے طور پر، سٹرنگز کا موازنہ لغوی طور پر کیا جاتا ہے ، اور نمبروں کا قدر کے لحاظ سے موازنہ کیا جاتا ہے۔
جاوا میں موازنہ کرنے والا - 2
جاوا 8 نے کچھ اچھی تبدیلیاں کیں۔ اگر ہم Comparator انٹرفیس کو قریب سے دیکھیں تو ہم دیکھیں گے کہ اس کے اوپر ایک تشریح موجود ہے @FunctionalInterface۔ درحقیقت، یہ تشریح معلومات کے لیے ہے اور اس کا مطلب ہے کہ یہ انٹرفیس فعال ہے۔ اس کا مطلب یہ ہے کہ اس انٹرفیس میں عمل درآمد کے بغیر صرف 1 تجریدی طریقہ ہے۔ یہ ہمیں کیا دیتا ہے؟ اب ہم موازنہ کوڈ اس طرح لکھ سکتے ہیں:
Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
قوسین میں یہ ہے کہ ہم متغیرات کو کیسے نام دیتے ہیں۔ جاوا خود اسے دیکھے گا کیونکہ ... اگر صرف ایک طریقہ ہے، تو یہ واضح ہے کہ کن ان پٹ پیرامیٹرز کی ضرورت ہے، کتنے، اور کون سی اقسام۔ اگلا، ہم ایک تیر کے ساتھ کہتے ہیں کہ ہم انہیں کوڈ کے اس حصے میں منتقل کرنا چاہتے ہیں۔ اس کے علاوہ، جاوا 8 کی بدولت، انٹرفیس میں پہلے سے طے شدہ طریقے نمودار ہوتے ہیں - یہ وہ طریقے ہیں جو پہلے سے طے شدہ (بذریعہ ڈیفالٹ) ظاہر ہوتے ہیں جب ہم کسی انٹرفیس کو نافذ کرتے ہیں۔ Comparator انٹرفیس میں ان میں سے کئی ہیں۔ مثال کے طور پر:
Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
ایک اور طریقہ ہے جو آپ کے کوڈ کو صاف ستھرا بنا دے گا۔ آئیے اوپر دی گئی مثال کو دیکھیں، جہاں ہم نے اپنے موازنہ کو بیان کیا ہے۔ وہ کیا کر رہا ہے؟ یہ کافی قدیم ہے۔ یہ صرف ایک چیز لیتا ہے اور اس سے کچھ قدر نکالتا ہے جو کہ موازنہ ہے۔ مثال کے طور پر، انٹیجر موازنہ کو لاگو کرتا ہے، لہذا ہم میسج آئی ڈی ویلیوز پر compareTo انجام دینے کے قابل تھے۔ اس سادہ کمپیریٹر فنکشن کو اس طرح بھی لکھا جا سکتا ہے:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
یعنی لفظی طور پر، "ہمارے پاس ایک Comparator ہے جو اس طرح موازنہ کرتا ہے: یہ اشیاء لیتا ہے، getId() طریقہ استعمال کرتے ہوئے ان سے موازنہ کرتا ہے، compareTo کا استعمال کرتے ہوئے موازنہ کرتا ہے۔" اور مزید خوفناک ڈیزائن نہیں۔ اور آخر میں، میں ایک اور خصوصیت نوٹ کرنا چاہوں گا۔ موازنہ کرنے والوں کو ایک ساتھ جکڑا جا سکتا ہے۔ مثال کے طور پر:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
comparator = comparator.thenComparing(obj -> obj.getMessage().length());

درخواست

موازنہ کرنے والا اعلان کافی منطقی نکلا، ہے نا؟ اب دیکھنا یہ ہے کہ اسے کس طرح اور کن جگہوں پر استعمال کیا جائے۔ → Collections.sort (java.util.Collections) یقیناً، ہم مجموعوں کو اس طرح ترتیب دے سکتے ہیں۔ لیکن سب کچھ نہیں، صرف فہرستیں. اور یہاں کچھ بھی غیر معمولی نہیں ہے، کیونکہ... یہ وہ فہرست ہے جس میں انڈیکس کے لحاظ سے کسی عنصر تک رسائی کی ضرورت ہوتی ہے۔ اور یہ عنصر نمبر دو کو عنصر نمبر تین کے ساتھ تبدیل کرنے کی اجازت دیتا ہے۔ لہذا، اس طرح ترتیب دینا صرف فہرستوں کے لیے ممکن ہے:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
Arrays.sort (java.util.Arrays) Arrays بھی ترتیب دینے کے لیے آسان ہیں۔ ایک بار پھر، انڈیکس کے ذریعہ عناصر تک رسائی کی اسی وجہ سے۔ → java.util.SortedSet اور java.util.SortedMap کی اولاد جیسا کہ ہمیں یاد ہے، سیٹ اور نقشہ ریکارڈ کو ذخیرہ کرنے کے آرڈر کی ضمانت نہیں دیتے ہیں۔ لیکن ہمارے پاس خصوصی نفاذ ہیں جو آرڈر کی ضمانت دیتے ہیں۔ اور اگر مجموعہ عناصر java.lang.Comparable کو لاگو نہیں کرتے ہیں، تو ہم Comparator کو اس طرح کے مجموعوں کے کنسٹرکٹر کو دے سکتے ہیں:
Set<Message> msgSet = new TreeSet(comparator);
اسٹریم API اسٹریم اے پی آئی میں، جو جاوا 8 میں ظاہر ہوا، ایک کمپیریٹر آپ کو اسٹریم عناصر پر کام کو آسان بنانے کی اجازت دیتا ہے۔ مثال کے طور پر، ہمیں 0 سے 999 تک کے بے ترتیب نمبروں کی ترتیب درکار ہے:
Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
ہم روک سکتے ہیں، لیکن مزید دلچسپ مسائل ہیں۔ مثال کے طور پر، آپ کو ایک نقشہ تیار کرنے کی ضرورت ہے، جہاں کلید میسج آئی ڈی ہے۔ ایک ہی وقت میں، ہم ان کیز کو ترتیب دینا چاہتے ہیں تاکہ سب سے چھوٹی سے بڑی تک چابیاں ترتیب میں ہوں۔ آئیے اس کوڈ کے ساتھ شروع کریں:
Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
جو ہم یہاں واپس حاصل کریں گے وہ دراصل ایک ہیش میپ ہے۔ اور جیسا کہ ہم جانتے ہیں، یہ کسی حکم کی ضمانت نہیں دیتا۔ لہذا، ہمارے ریکارڈ کو ID کے لحاظ سے ترتیب دیا گیا ہے، آسانی سے ترتیب سے باہر ہو گیا. اچھا نہیں. ہمیں اپنے کلکٹر کو تھوڑا سا تبدیل کرنا پڑے گا:
Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg, (oldValue, newValue) -> oldValue, TreeMap::new));
کوڈ تھوڑا سا خوفناک لگ رہا تھا، لیکن TreeMap کے واضح نفاذ کی بدولت مسئلہ اب صحیح طریقے سے حل ہو گیا تھا۔ آپ یہاں مختلف گروپس کے بارے میں مزید پڑھ سکتے ہیں: آپ کلیکٹر خود بنا سکتے ہیں۔ آپ یہاں مزید پڑھ سکتے ہیں: "جاوا 8 میں ایک کسٹم کلیکٹر بنانا" ۔ اور یہاں بحث کو پڑھنا مفید ہے: "جاوا 8 فہرست کو ندی کے ساتھ نقشہ بنانے کے لیے" ۔
جاوا میں موازنہ کرنے والا - 3
موازنہ اور موازنہ ریک اچھے ہیں۔ لیکن ان کے ساتھ منسلک ایک nuance ہے جو یاد رکھنے کے قابل ہے. جب کوئی کلاس چھانٹتی ہے، تو یہ حساب لگاتی ہے کہ وہ آپ کی کلاس کو Comparable میں کاسٹ کر سکتی ہے۔ اگر ایسا نہیں ہے تو، آپ کو پھانسی کے وقت ایک غلطی موصول ہوگی۔ آئیے ایک مثال دیکھتے ہیں:
SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
ایسا لگتا ہے کہ یہاں کچھ بھی غلط نہیں ہے۔ لیکن درحقیقت، ہماری مثال میں، یہ خرابی کے ساتھ کریش ہو جائے گا: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable اور یہ سب اس لیے کہ اس نے عناصر کو ترتیب دینے کی کوشش کی (آخر کار یہ ایک ترتیب شدہ سیٹ ہے)۔ اور میں نہیں کر سکا۔ SortedMap اور SortedSet کے ساتھ کام کرتے وقت آپ کو یہ یاد رکھنا چاہیے۔ اضافی طور پر دیکھنے کے لیے تجویز کردہ: Yuri Tkach: HashSet اور TreeSet - مجموعے #1 - Advanced Java
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION