6. ہمیں فہرست کے بارے میں بتائیں
فہرست ایک انٹرفیس ہے جو اشیاء کی ترتیب شدہ ساخت کی نمائندگی کرتا ہے، جسے فہرست کہا جاتا ہے۔ اس ڈھانچے کی "ٹرک" یہ ہے کہ فہرست میں موجود عناصر کو اشاریہ کے ذریعے داخل، تبدیل یا حذف کیا جا سکتا ہے، یعنی فہرست کا اندرونی شناخت کنندہ ۔ دوسرے الفاظ میں، انڈیکس کا مطلب ہے: "فہرست کے آغاز سے کتنے عناصر ہیں۔" فہرست کے پہلے عنصر میں انڈیکس 0 ہے، دوسری میں انڈیکس 1 ہے، وغیرہ۔ لہذا پانچواں عنصر فہرست کے آغاز سے چار عناصر دور ہے۔ جیسا کہ اوپر ذکر کیا گیا ہے، فہرست میں اشیاء کو شامل کرنے کی ترتیب اہم ہے۔ اسی لیے ڈیٹا کی ساخت کو فہرست کہا جاتا ہے ۔ ہم اس ڈھانچے کے منفرد طریقوں کی فہرست بناتے ہیں جن کا مقصد انڈیکس کے لحاظ سے عناصر کے ساتھ کام کرنا ہے:- حاصل کریں - عنصر کو مخصوص پوزیشن پر لوٹاتا ہے (انڈیکس ویلیو کے لحاظ سے)،
- ہٹانا - مخصوص پوزیشن پر عنصر کو ہٹاتا ہے،
- سیٹ - مخصوص پوزیشن پر عنصر کو طریقہ میں بیان کردہ عنصر سے بدل دیتا ہے۔
- push - گزرے ہوئے عنصر کو اسٹیک کے اوپری حصے میں شامل کرتا ہے۔
- جھانکنا - وہ عنصر لوٹاتا ہے جو اسٹیک کے اوپر ہوتا ہے۔
- pop - وہ عنصر بھی لوٹاتا ہے جو اسٹیک کے اوپر ہے، لیکن اسے ہٹا دیتا ہے۔
- empty - چیک کرتا ہے کہ آیا اسٹیک خالی ہے - سچ ہے یا نہیں - غلط ؛
- تلاش - دیئے گئے عنصر کے اسٹیک کو تلاش کرتا ہے۔ اگر کوئی عنصر پایا جاتا ہے، تو اسٹیک کے اوپری حصے کے نسبت اس کی ترتیب نمبر لوٹا دی جاتی ہے۔ اگر عنصر نہیں ملتا ہے، تو قدر -1 واپس آ جاتی ہے۔
7. ہمیں نقشہ کے بارے میں بتائیں
جیسا کہ اوپر بتایا گیا ہے، نقشہ ایک مجموعہ ہے جس میں انٹرفیس اور ان کے نفاذ کا ایک الگ ڈھانچہ ہوتا ہے۔ یہ الگ ہے کیونکہ یہاں قدریں ایک وقت میں ایک نہیں بلکہ ایک "کلیدی قدر" کے جوڑے میں محفوظ کی جاتی ہیں۔ بنیادی نقشہ کے طریقے :- put(K کلید، V قدر) - نقشے میں ایک عنصر شامل کرنا؛
- حاصل کریں (آبجیکٹ کی) - کلید کے ذریعہ ایک قدر تلاش کریں۔
- containsKey(Object key) — اس کلید کی موجودگی کے لیے نقشہ چیک کرتا ہے۔
- containsValue(آبجیکٹ ویلیو) - اس قدر کی موجودگی کے لیے نقشہ چیک کرتا ہے۔
- ہٹائیں (آبجیکٹ کی) - کسی قدر کو اس کی کلید سے ہٹانا۔
- HashMap - بے ترتیب ترتیب میں اقدار کو ذخیرہ کرنے کے لیے ڈیزائن کیا گیا ہے، لیکن آپ کو نقشہ کے عناصر کو تیزی سے تلاش کرنے کی اجازت دیتا ہے۔ آپ کو null مطلوبہ لفظ کا استعمال کرتے ہوئے ایک کلید کی وضاحت کرنے کی اجازت دیتا ہے ، لیکن ایک بار سے زیادہ نہیں، کیونکہ ایک ہی چابیاں والے جوڑے ایک دوسرے کے اوپر لکھے ہوئے ہیں۔ اہم شرط چابیاں کی انفرادیت ہے: اقدار کو دہرایا جاسکتا ہے (کئی کالعدم اقدار ہوسکتی ہیں)۔
- LinkedHashMap HashMap کا ایک اینالاگ ہے جو اقدار کو اس ترتیب میں اسٹور کرتا ہے جس ترتیب سے وہ شامل کیے گئے تھے۔ اس کے مطابق، LinkedList کی طرح ، اس کا ایک ہیڈر ہے - ایک دوہری لنک شدہ فہرست کا سربراہ۔ جب شروع کیا جاتا ہے، تو یہ خود کی طرف اشارہ کرتا ہے۔
LinkedHashMap کے پاس ایک AccessOrder بھی ہے جو یہ بتاتا ہے کہ جب iterator استعمال کیا جائے گا تو عناصر تک کیسے رسائی حاصل کی جائے گی۔ اگر AccessOrder غلط ہے تو رسائی اسی ترتیب سے کی جائے گی جس ترتیب سے عناصر داخل کیے گئے تھے۔ اگر درست ہے تو، عناصر آخری رسائی کی ترتیب میں ہوں گے (آخری رسائی شدہ عنصر آخر میں رکھا جائے گا)۔
- TreeMap ایک نقشہ ہے جو عناصر کو کلیدی اقدار کے مطابق ترتیب دیتا ہے۔ TreeSet کی طرح ، لیکن کلیدی اقدار پر مبنی جوڑوں کے لیے۔ TreeMap چھانٹنے کے اصول مرتب کرنے کے لیے ، کلیدوں کو موازنہ انٹرفیس کو لاگو کرنا چاہیے ۔ بصورت دیگر، ایک کلیدی اورینٹڈ کمپیریٹر ہونا چاہیے (وہ جو TreeMap کنسٹرکٹر میں بیان کیا گیا ہے )، ایک TreeSet - ایک TreeMap آبجیکٹ کے ساتھ لاگو کیا جائے، جس میں، حقیقت میں، تمام جادو ہوتا ہے۔
آپ TreeMap کی خصوصیات کے بارے میں مضمون میں سرخ سیاہ درختوں کا استعمال کرتے ہوئے TreeMap میں چھانٹنے کے بارے میں مزید پڑھ سکتے ہیں ۔
- Hashtable HashMap سے ملتا جلتا ہے ، لیکن nulls کو کلیدوں یا قدروں کے بطور ذخیرہ کرنے کی اجازت نہیں دیتا ہے ۔ اسے ملٹی تھریڈنگ نقطہ نظر سے احتیاط سے ہم آہنگ کیا گیا ہے، جس کا مطلب یہ ہے کہ یہ ملٹی تھریڈنگ نقطہ نظر سے محفوظ ہے۔ لیکن یہ نفاذ پرانا اور سست ہے، لہذا اب آپ کو کم و بیش نئے پروجیکٹس میں Hashtable نظر نہیں آئے گا ۔
8. ArrayList بمقابلہ LinkedList۔ کون سا استعمال کرنا افضل ہے؟
یہ سوال شاید ڈیٹا ڈھانچے پر سب سے زیادہ مقبول ہے اور اس کے ساتھ کچھ نقصانات بھی ہیں۔ اس کا جواب دینے سے پہلے، آئیے ان ڈیٹا ڈھانچے کے بارے میں مزید جانیں۔ ArrayList فہرست انٹرفیس کو لاگو کرتا ہے اور ایک اندرونی صف پر کام کرتا ہے جسے ضرورت کے مطابق بڑھایا جاتا ہے۔ جب اندرونی صف مکمل طور پر بھر جاتی ہے، اور ایک نیا عنصر داخل کرنے کی ضرورت ہوتی ہے، تو (پرانے سائز * 1.5) +1 کے سائز کے ساتھ ایک نئی صف بنائی جاتی ہے۔ اس کے بعد، پرانی صف سے تمام ڈیٹا کو نئے + نئے عنصر میں کاپی کیا جاتا ہے، اور پرانے کو ردی کی ٹوکری جمع کرنے والے کے ذریعے حذف کر دیا جائے گا ۔ شامل کرنے کا طریقہ سرنی کے آخری خالی سیل میں ایک عنصر شامل کرتا ہے۔ یعنی، اگر ہمارے پاس پہلے سے ہی 3 عناصر موجود ہیں، تو یہ اگلے ایک کو چوتھے سیل میں شامل کر دے گا۔ آئیے بنیادی طریقوں کی کارکردگی کو دیکھتے ہیں:- get(int index) - کسی عنصر کو انڈیکس کے لحاظ سے ایک صف میں لینا O(1) میں سب سے تیز ہے ؛
- add(Object obj) - اگر کسی نئے عنصر کے لیے اندرونی صف میں کافی جگہ ہے، تو ایک عام اندراج کے ساتھ O(1) وقت گزارا جائے گا ، کیونکہ اضافے کو آخری سیل تک نشانہ بنایا جاتا ہے۔
اگر ہمیں ایک نئی صف بنانے اور اس میں مواد کو کاپی کرنے کی ضرورت ہے، تو ہمارا وقت سرنی O(n) میں عناصر کی تعداد کے براہ راست متناسب ہوگا ۔
- remove(int index) - کسی عنصر کو ہٹاتے وقت، مثال کے طور پر، درمیان سے، ہمیں O(n/2) وقت ملے گا، کیونکہ ہمیں عناصر کو اس کے دائیں طرف ایک سیل پیچھے منتقل کرنے کی ضرورت ہوگی۔ اس کے مطابق، اگر فہرست کے آغاز سے حذف کر رہے ہیں، تو O(n)، آخر سے - O(1)؛
- add(int index, Object obj) - ڈیلیٹ کرنے کی طرح کی صورتحال: درمیان میں شامل کرتے وقت، ہمیں عناصر کو دائیں ایک سیل کو آگے بڑھانا ہوگا، اس لیے وقت O(n/2) ہے۔ یقینا، شروع سے - O(n)، آخر سے - O(1)؛
- set(int index, Object obj) - یہاں صورت حال مختلف ہے، کیونکہ آپ کو صرف مطلوبہ عنصر تلاش کرنے اور باقی کو منتقل کیے بغیر اس پر لکھنے کی ضرورت ہے، لہذا O(1)۔
- get(int index) - جب فہرست کے وسط میں موجود کسی عنصر کو تلاش کرتے ہیں، تو یہ تمام عناصر کو ترتیب سے تلاش کرنا شروع کر دیتا ہے جب تک کہ مطلوبہ چیز نہ مل جائے۔ منطقی طور پر، تلاش کو O(n/2) لینا چاہیے ، لیکن LinkedList کی بھی ایک دم ہوتی ہے، اس لیے تلاش دونوں اطراف سے بیک وقت کی جاتی ہے۔ اس کے مطابق، وقت کو گھٹا کر O(n/4) کر دیا گیا ہے ۔
اگر عنصر فہرست کے آغاز یا اختتام کے قریب ہے، تو وقت ہوگا O(1) ؛
- add(Object obj) - ایک نیا عنصر شامل کرتے وقت، "ٹیل" عنصر میں اگلے عنصر کا ایک لنک شامل ہوگا، اور نئے کو اس پچھلے عنصر کا لنک ملے گا اور نیا "ٹیل" بن جائے گا۔ اس کے مطابق، وقت O(1) ہوگا ؛
- remove(int index) - get(int index) طریقہ کی طرح کی منطق ۔ فہرست کے بیچ سے کسی عنصر کو ہٹانے کے لیے، آپ کو پہلے اسے تلاش کرنا ہوگا۔ یہ پھر O(n/4) ہے ، جبکہ حذف کرنے سے خود کچھ نہیں ہوتا، کیونکہ یہ صرف پڑوسی اشیاء کے پوائنٹر کو تبدیل کرتا ہے (وہ ایک دوسرے کا حوالہ دینا شروع کر دیتے ہیں)۔ اگر عنصر شروع یا آخر میں ہے، تو پھر - O(1) ؛
- add(int index, Object obj) اور set(int index, Object obj) - طریقوں میں وقت کی پیچیدگی ایک جیسی ہوگی get(int index) ، کیونکہ زیادہ تر وقت عنصر کی تلاش میں صرف ہوتا ہے۔ لہذا، فہرست کے وسط کے لیے - O(n/4) ، آغاز کے لیے - O(1)۔
آپریشن | ArrayList | لنکڈ لسٹ |
---|---|---|
انڈیکس کے ذریعہ حاصل کریں (اشاریہ) | O(1) | درمیان میں O(n/4) |
ایک نیا عنصر شامل کریں (obj) |
O(1) اگر آپ کو ایک صف کاپی کرنے کی ضرورت ہے، تو - O(n) |
O(1) |
عنصر کو ہٹا دیں (انٹ انڈیکس) |
شروع سے - O(n) درمیان سے - O(n/2) آخر سے - O(1) |
درمیان میں - O(n/4) آخر میں یا شروع میں - O(n) |
عنصر شامل کریں (انٹ انڈیکس، آبجیکٹ آبجیکٹ) |
اوپر واپس جائیں - O(n) وسط تک - O(n/2) آخر تک - O(1) |
درمیان میں - O(n/4) آخر میں یا شروع میں - O(n) |
عنصر سیٹ کو تبدیل کریں (انڈیکس، اعتراض) | O(1) |
درمیان میں - O(n/4) آخر میں یا شروع میں - O(n) |
- بہترین انتخاب اگر سب سے زیادہ کثرت سے آپریشن کسی عنصر کی تلاش کر رہا ہو، کسی عنصر کو اوور رائٹ کر رہا ہو؛
- سب سے خراب انتخاب اگر آپریشن شروع-وسط میں داخل کرنا اور حذف کرنا ہے، کیونکہ دائیں طرف عناصر کی شفٹ کی کارروائیاں ہوں گی۔
- بہترین انتخاب اگر ہمارا بار بار آپریشن شروع اور درمیان میں اندراج اور حذف کرنا ہے۔
- اگر سب سے عام آپریشن تلاش کرنا ہو تو بدترین انتخاب۔
9. ہیش میپ میں عناصر کیسے محفوظ کیے جاتے ہیں؟
HashMap مجموعہ میں ایک اندرونی سرنی Node[] ٹیبل ہے ، جس کے سیلز کو buckets بھی کہا جاتا ہے ۔ نوڈ پر مشتمل ہے:- کلید - کلید کا لنک،
- قدر - قدر کا حوالہ،
- ہیش - ہیش ویلیو،
- اگلا - اگلے نوڈ کا لنک ۔
- کلید کو null کے لیے چیک کیا گیا ہے ۔ اگر یہ null ہے تو کلید کو ٹیبل[0] سیل میں محفوظ کیا جائے گا کیونکہ null کے لیے ہیش کوڈ ہمیشہ 0 ہوتا ہے۔
- اگر کلید null نہیں ہے ، تو کلیدی آبجیکٹ کے hashcode() طریقہ کو کہا جاتا ہے ، جو اس کا ہیش کوڈ واپس کر دے گا۔ یہ ہیش کوڈ سرنی سیل کا تعین کرنے کے لیے استعمال کیا جاتا ہے جہاں نوڈ آبجیکٹ کو محفوظ کیا جائے گا۔
- اس کے بعد، اس ہیش کوڈ کو اندرونی hash() طریقہ میں رکھا جاتا ہے، جو hashcode کا حساب لگاتا ہے، لیکن table[] array کے سائز کے اندر ۔
- اگلا، ہیش ویلیو پر منحصر ہے، نوڈ کو ٹیبل[] سرنی میں ایک مخصوص سیل میں رکھا جاتا ہے ۔
- اگر موجودہ نوڈ عنصر کو محفوظ کرنے کے لیے استعمال ہونے والا ٹیبل[] سیل خالی نہیں ہے، لیکن اس میں پہلے سے کچھ عنصر موجود ہے، تو نوڈ کے عناصر کو اگلی قدر پر دہرایا جاتا ہے جب تک کہ آخری عنصر تک نہ پہنچ جائے۔ یعنی وہ جس کا اگلا فیلڈ null ہے ۔
اس تلاش کے دوران، محفوظ نوڈ آبجیکٹ کی کلید کا موازنہ ان کیز کے ساتھ کیا جاتا ہے جو تلاش کی جا رہی ہیں:
- اگر کوئی میچ پایا جاتا ہے، تو تلاش ختم ہو جائے گی، اور نیا نوڈ اس نوڈ کو اوور رائٹ کر دے گا جس میں میچ ملا تھا (صرف اس کی ویلیو فیلڈ کو اوور رائٹ کیا جائے گا )؛
- اگر کوئی کلیدی مماثلت نہیں ملتی ہے، تو نیا نوڈ اس فہرست میں آخری بن جائے گا، اور پچھلا نوڈ اس کا اگلا لنک ہوگا۔
10. تکرار کرنے والے کے بارے میں وضاحت کریں۔
اوپر دیے گئے کلیکشن ہائرارکی میپنگ ڈایاگرام میں ، کلیکشن انٹرفیس وہیں تھا جہاں سے پورا درجہ بندی شروع ہوئی تھی، لیکن عملی طور پر یہ بالکل ایسا نہیں ہے۔ مجموعہ ایک انٹرفیس سے ایک iterator() طریقہ کے ساتھ وراثت میں ملتا ہے ، جو ایک ایسی چیز واپس کرتا ہے جو Iterator<E> انٹرفیس کو لاگو کرتا ہے ۔ Iterator انٹرفیس اس طرح لگتا ہے:public interface Iterator <E>{
E next();
boolean hasNext();
void remove();
}
next() - اس طریقہ کو کال کرکے، آپ اگلا عنصر حاصل کرسکتے ہیں۔ hasNext() - آپ کو یہ جاننے کی اجازت دیتا ہے کہ آیا کوئی اگلا عنصر موجود ہے، اور آیا مجموعہ کا اختتام ہوچکا ہے۔ اور جب عناصر موجود ہوں گے تو hasNext() واپس آجائے گا true ۔ عام طور پر، hasNext() کو next() طریقہ سے پہلے کہا جاتا ہے ، کیونکہ next() مجموعہ کے اختتام پر پہنچنے پر NoSuchElementException پھینک دے گا ۔ remove() - اس عنصر کو ہٹاتا ہے جو next() پر آخری کال کے ذریعے حاصل کیا گیا تھا ۔ Iterator کا مقصد عناصر پر تکرار کرنا ہے۔ مثال کے طور پر:
Set<Integer> values = new TreeSet<>();
values.add(5);
values.add(3);
values.add(6);
values.add(8);
values.add(2);
values.add(4);
values.add(1);
values.add(7);
Iterator<Integer> iter = values.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
دراصل، ہر ایک کے لیے لوپ کو ہڈ کے نیچے ایٹریٹر کا استعمال کرتے ہوئے لاگو کیا جاتا ہے۔ آپ یہاں اس کے بارے میں مزید پڑھ سکتے ہیں ۔ فہرست ایک تکرار کرنے والے کا اپنا ورژن فراہم کرتی ہے، لیکن ایک ٹھنڈا اور زیادہ نفیس - ListIterator ۔ یہ انٹرفیس Iterator کو بڑھاتا ہے اور اس میں اضافی طریقے ہیں:
- hasPrevious صحیح لوٹ آئے گا اگر مجموعہ میں کوئی سابقہ عنصر ہے، ورنہ غلط؛
- پچھلا موجودہ عنصر کو لوٹاتا ہے اور پچھلے ایک پر چلا جاتا ہے۔ اگر کوئی نہیں ہے، تو NoSuchElementException پھینک دیا جاتا ہے۔
- add اس عنصر سے پہلے پاس شدہ آبجیکٹ داخل کرے گا جسے اگلی کال کے ذریعے واپس کیا جائے گا next() ;
- سیٹ موجودہ عنصر کو پاس کردہ آبجیکٹ کا حوالہ تفویض کرتا ہے۔
- nextIndex اگلے عنصر کا انڈیکس لوٹاتا ہے۔ اگر ایسی کوئی چیز نہیں ہے، تو فہرست کا سائز واپس کر دیا جاتا ہے؛
- previousIndex پچھلے عنصر کا انڈیکس لوٹاتا ہے۔ اگر کوئی نہیں ہے، تو نمبر -1 واپس آ جاتا ہے۔
GO TO FULL VERSION