89. كيف تختلف قائمة ArrayList عن قائمة LinkedList؟
يعد هذا أحد الأسئلة الأكثر شيوعًا، إلى جانب السؤال المتعلق بالبنية الداخلية لـ HashMap . لا تكتمل أي مقابلة بدونها، لذا يجب أن تنطق إجابتك على لسانك بسهولة. بالإضافة إلى ما هو واضح (لديهم أسماء مختلفة)، فهي تختلف في بنيتها الداخلية. لقد ناقشنا سابقًا البنية الداخلية لكل من ArrayList و LinkedList ، لذا لن أتعمق في تفاصيل التنفيذ الخاصة بهما. سأذكرك فقط أنه يتم تنفيذ ArrayList باستخدام مصفوفة داخلية يزيد حجمها ديناميكيًا وفقًا لهذه الصيغة:<size of the current array> * 3 / 2 + 1
بالإضافة إلى ذلك، يستخدم تنفيذ LinkedList قائمة داخلية مرتبطة بشكل مزدوج، أي أن كل عنصر له إشارة إلى العناصر السابقة والتالية، باستثناء العناصر الموجودة في بداية القائمة ونهايتها. يحب القائمون على المقابلات طرح هذا السؤال مثل هذا، "أيهما أفضل، ArrayList أم LinkedList ؟" على أمل اللحاق بك. ففي النهاية، إذا قلت أن أحدهما أفضل، فقد أعطيت إجابة خاطئة. بدلاً من ذلك، يجب عليك توضيح الموقف المحدد الذي تتحدث عنه: الوصول إلى العناصر عن طريق الفهرس أو الإدراج في منتصف القائمة. وبعد ذلك، اعتمادًا على إجابتهم، يمكنك توضيح أيهما أفضل. لقد وصفت سابقًا كيفية عمل ArrayList و LinkedList في كل موقف. دعونا نلخص ذلك من خلال وضعها في صف واحد للمقارنة: إضافة عنصر (إضافة)
-
إذا لم يتم تحديد فهرس، فسيتم إضافة عنصر جديد تلقائيًا إلى النهاية لكلا النوعين من القوائم. في LinkedList ، سيصبح العنصر الجديد هو الذيل الجديد (ستتم إعادة كتابة زوج من المراجع فقط، وبالتالي فإن التعقيد الخوارزمي هو O(1) ).
تضيف طريقة الإضافة عنصرًا إلى آخر خلية فارغة في المصفوفة ( O(1) ).
-
إن إضافة عنصر حسب الفهرس يعني عادةً إدراجه في مكان ما في منتصف القائمة. في LinkedList ، ستقوم الطريقة أولاً بالبحث عن الموقع المطلوب عن طريق التكرار فوق العناصر من الذيل والرأس ( O(n/2) ) ثم ستقوم بإدراج القيمة عن طريق الكتابة فوق مراجع العناصر على كلا الجانبين حيث تم إدراج عنصر جديد ( O(1) ). سيكون التعقيد الخوارزمي الإجمالي لهذه العملية هو O(n/2) .
في نفس الموقف (الإضافة حسب الفهرس)، تبحث ArrayList عن الموقع المطلوب ( O(1) ) ثم تقوم بنقل جميع العناصر الموجودة إلى اليمين (بما في ذلك العنصر المخزن بالفعل في الفهرس المحدد) إلى اليمين بمقدار عنصر واحد (وهو ما قد يتطلب إنشاء مصفوفة داخلية جديدة ونسخ العناصر إليها) ( O(n/2) ). التعقيد العام هو O(n/2) .
-
إن إضافة عنصر إلى بداية LinkedList يشبه إضافة عنصر إلى النهاية: يصبح العنصر الجديد هو الرأس الجديد ( O(1) ). لكن بالنسبة لقائمة ArrayList، تتطلب هذه العملية نقل جميع العناصر إلى اليمين ( O(n) ).
90. كيف تختلف قائمة ArrayList عن HashSet؟
إذا تمكنا من مقارنة ArrayList و LinkedList على أساس كل عملية على حدة لتحديد أيهما أفضل، فلن نجد أنه من السهل إجراء مثل هذه المقارنة بين ArrayList و HashSet ، لأنهما مجموعتان مختلفتان تمامًا. يمكنك مقارنة حلوى بأخرى، لكن مقارنة الحلوى والطبق المالح يمثل تحديًا - فهما مختلفان بشكل مؤلم. ومع ذلك سأحاول أن أشير إلى بعض الفروق بينهما:-
يقوم ArrayList بتنفيذ واجهة القائمة بينما يقوم HashSet بتنفيذ واجهة Set .
-
يتيح لك ArrayList الوصول إلى عنصر عن طريق الفهرس: تحتوي عملية get على تعقيد خوارزمي O(1) ، لكن HashSet يتيح لك فقط الوصول إلى العنصر المطلوب عن طريق التكرار، مما ينتج عنه تعقيد خوارزمي يتراوح من O(1) إلى O(n) .
-
يسمح ArrayList بالعناصر المكررة. في HashSet ، جميع العناصر فريدة: أي محاولة لإضافة عنصر موجود بالفعل في HashSet ستفشل (يتم التحقق من التكرارات بواسطة رمز التجزئة، ومن هنا جاء اسم هذه المجموعة).
-
يتم تنفيذ ArrayList باستخدام مصفوفة داخلية، ولكن يتم تنفيذ HashSet باستخدام HashMap داخلي .
-
تحافظ ArrayList على ترتيب إدراج العناصر، لكن HashSet عبارة عن مجموعة غير مرتبة ولا تحافظ على ترتيب العناصر.
-
يسمح ArrayList بأي عدد من القيم الخالية، ولكن يمكنك فقط إضافة قيمة فارغة واحدة إلى HashSet (بعد كل شيء، يجب أن تكون العناصر فريدة).
91. لماذا تمتلك Java العديد من تطبيقات المصفوفات الديناميكية المختلفة؟
هذا هو أكثر من سؤال فلسفي. يمكننا أيضًا أن نتساءل لماذا يأتون بهذا العدد الكبير من التقنيات الجديدة والمتنوعة؟ للراحة. وينطبق الشيء نفسه على عدد كبير من تطبيقات المصفوفة الديناميكية. لا يمكن تسمية أي منها بالتنفيذ الأفضل أو المثالي. ولكل منها مزاياه في حالات محددة. مهمتنا هي معرفة الاختلافات بينهم ونقاط القوة والضعف لديهم حتى نتمكن من استخدام المجموعة الأكثر ملاءمة لأي موقف معين.92. لماذا تمتلك Java العديد من تطبيقات تخزين القيمة الأساسية المختلفة؟
الوضع هنا هو نفسه كما هو الحال مع تطبيقات المصفوفة الديناميكية. من المؤكد أنه لا يوجد أي شخص أفضل من الآخرين على مستوى العالم: فكل منهم لديه نقاط قوة ونقاط ضعف. ويجب علينا بالطبع الاستفادة القصوى من نقاط قوتهم. مثال: الحزمة المتزامنة، التي تحتوي على العديد من الفئات متعددة الخيوط، لديها مجموعات متزامنة خاصة بها. تتمتع فئة ConcurrentHashMap بميزة على HashMap القياسية من حيث الأمان عند العمل مع البيانات في بيئة متعددة الخيوط، ولكن ذلك يأتي على حساب أداء أبطأ. ويتوقف تدريجيًا استخدام التطبيقات التي لا تمثل الخيار الأفضل في أي موقف. على سبيل المثال: تم نسيان Hashtable ، الذي كان من المفترض في الأصل أن يكون HashMap آمنًا لسلسلة العمليات ، ولم يعد صالحًا للاستخدام، لأن ConcurrentHashMap أفضل من Hashtable عند العمل في بيئة متعددة الخيوط.93. كيف يمكنني فرز مجموعة من العناصر؟
أول شيء يجب قوله هو أن الفئة التي تمثل عناصر المجموعة يجب أن تنفذ الواجهة القابلة للمقارنة ، والتي تتكون من طريقة المقارنة . أو تحتاج إلى فئة تطبق واجهة المقارنة ، بما في ذلك طريقة المقارنة الخاصة بها. تشير كلتا الطريقتين إلى كيفية مقارنة الكائنات من نوع معين. يعد هذا أمرًا بالغ الأهمية عند الفرز، لأن خوارزمية الفرز تحتاج إلى فهم المبدأ الذي يجب استخدامه لمقارنة العناصر. يتم ذلك بشكل أساسي عن طريق تطبيق Comparable مباشرةً في الفصل الذي تريد فرزه. يعد استخدام المقارنة أقل شيوعًا. لنفترض أنك تستخدم فئة من بعض المكتبات ولا تقوم بتنفيذ Comparable ، لكنك تحتاج إلى فرز مجموعة من كائناتها. نظرًا لأنه لا يمكنك تغيير كود هذه الفئة (إلا عن طريق توسيعها)، يمكنك كتابة تطبيق للمقارنة يشير إلى كيفية مقارنة كائنات الفئة. ومثال آخر. إذا كنت بحاجة إلى فرز كائنات من نفس النوع بطرق مختلفة، فيمكنك كتابة تطبيقات مقارنة متعددة لاستخدامها في مواقف مختلفة. كقاعدة عامة، العديد من الفئات الجاهزة، على سبيل المثال String ، تقوم بالفعل بتنفيذ الواجهة القابلة للمقارنة . هذا يعني أنه لا داعي للقلق بشأن كيفية مقارنة هذه الفئات. يمكنك فقط المضي قدمًا واستخدامها. الطريقة الأولى والأكثر وضوحًا هي استخدام فئة TreeSet أو TreeMap . تقوم هذه الفئات بتخزين العناصر بترتيب مرتبة بناءً على المقارنة التي تنفذها عناصر الفئة. لا تنس أن TreeMap يقوم بفرز المفاتيح، وليس القيم. إذا كنت تستخدم Comparator بدلاً من Comparable ، فستحتاج إلى تمرير كائن Comparator إلى مُنشئ المجموعة عند إنشائه:TreeSet treeSet = new TreeSet(customComparator);
ولكن ماذا لو كان لديك نوع مختلف من المجموعة؟ كيف يمكنك فرز ذلك؟ في هذه الحالة، الطريقة الثانية لفئة الأداة المساعدة Collections — طريقة sort() — مناسبة. الطريقة ثابتة، لذا كل ما تحتاجه هو إضافة اسم الفصل في المقدمة ثم تمرير القائمة ليتم فرزها. على سبيل المثال:
Collections.sort(someList);
إذا كنت تستخدم تطبيق Comparator بدلاً من Comparable ، فأنت بحاجة إلى تمريره كوسيطة ثانية:
Collections.sort(someList, customComparator);
ستعمل هذه العملية على تغيير الترتيب الداخلي للعناصر في القائمة التي تم تمريرها: سيتم فرز القائمة باستخدام المقارنة. لاحظ أن القائمة التي تم تمريرها يجب أن تكون قابلة للتغيير، وإلا ستفشل الطريقة وستطرح UnsupportedOperationException . الخيار الثالث هو استخدام طريقة الفرز الخاصة بفئة الدفق ، والتي تقوم بفرز عناصر المجموعة. إذا كنا نستخدم Comparable :
someList = someList.stream().sorted().collect(Collectors.toList());
إذا كنا نستخدم المقارنة :
someList = someList.stream().sorted(customComparator).collect(Collectors.toList());
الطريقة الرابعة هي تنفيذ خوارزمية الفرز يدويًا، على سبيل المثال، فرز الفقاعات
أو فرز الدمج
.
فئة الكائن. يساوي () و hashCode ()
94. قدم وصفًا موجزًا لفئة الكائن في Java.
في الجزء الثاني من المراجعة، ناقشنا بالفعل أساليب فئة الكائن . سأذكرك هنا أن فئة الكائن هي سلف كل فئة في Java. وله 11 طريقة، وهي بدورها موروثة من قبل جميع الطبقات.95. ما هو يساوي () و hashCode () المستخدم في جافا؟
hashCode () هي طريقة لفئة الكائن التي ترثها جميع الفئات. وتتمثل مهمتها في إنشاء رقم يمثل كائنًا محددًا. يمكن العثور على مثال على هذه الطريقة في HashMap ، حيث يتم استدعاؤها على الكائنات الرئيسية للحصول على رمز التجزئة المحلي، والذي سيحدد المجموعة (خلية المصفوفة الداخلية) التي سيتم تخزين زوج القيمة الرئيسية فيها. تُستخدم هذه الطريقة بشكل عام في طريقة يساوي () كإحدى طرقها الرئيسية لتحديد الكائنات. يساوي () هي طريقة لفئة الكائن التي تتمثل مهمتها في مقارنة الكائنات وتحديد ما إذا كانت متساوية. يتم استخدام هذه الطريقة في كل مكان نحتاج فيه إلى مقارنة الكائنات، لأن معامل المقارنة القياسي == غير مناسب للكائنات، لأنه يقارن فقط مراجع الكائنات.96. أخبرنا عن العقد بين يساوي () و hashCode () في جافا؟
أولاً، اسمحوا لي أن أقول أنه لكي تعمل أساليب يساوي () و hashCode () بشكل صحيح، يجب أن يتم تجاوزها بشكل صحيح. يجب أن تتبع تطبيقاتها الجديدة هذه القواعد:- يجب أن تحتوي الكائنات المتطابقة التي تُرجع تساويها بشكل صحيح على نفس رموز التجزئة.
- الكائنات التي لها نفس رموز التجزئة ليست بالضرورة متساوية.
GO TO FULL VERSION