CodeGym /مدونة جافا /Random-AR /استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java. ...
John Squirrels
مستوى
San Francisco

استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java. الجزء 10

نشرت في المجموعة
أهلاً! كم ساعة يستغرق الأمر لتصبح سيدًا في شيء ما؟ لقد سمعت في كثير من الأحيان شيئًا مثل: "لكي تصبح محترفًا في أي شيء، عليك أن تقضي 10000 ساعة عليه". هذا رقم مخيف، أليس كذلك؟ استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java.  الجزء 10 - 1ومع ذلك، أتساءل عما إذا كان هذا صحيحا. وأحاول باستمرار معرفة عدد الساعات التي استثمرتها بالفعل في إتقان فن البرمجة. وعندما أتجاوز هذا الخط الخاص المكون من 10000 ساعة وأصبح سيدًا، هل سأشعر بالفرق؟ أم أنني تجاوزت هذا الخط بالفعل منذ فترة طويلة دون أن أدرك ذلك؟ وفي كلتا الحالتين، ليس عليك استثمار الكثير من الوقت لتصبح مبرمجًا. الشيء المهم هو استخدام وقتك بحكمة. هدفك الأساسي هو الحصول على مقابلة. وفي المقابلات، يُسأل مطورو البرمجيات المحتملون أولاً عن النظرية، لذلك يجب أن يكون ذلك بمثابة نقطة قوة. في الواقع، عندما تستعد للمقابلة، فإن مهمتك هي اكتشاف جميع الفجوات في المعرفة بنظرية Java الأساسية ومن ثم ملؤها. اليوم أنا هنا لمساعدتك على القيام بذلك، حيث سنواصل اليوم مراجعتنا لأسئلة المقابلة الأكثر شيوعًا. حسنا، دعونا نستمر!

89. كيف تختلف قائمة ArrayList عن قائمة LinkedList؟

يعد هذا أحد الأسئلة الأكثر شيوعًا، إلى جانب السؤال المتعلق بالبنية الداخلية لـ HashMap . لا تكتمل أي مقابلة بدونها، لذا يجب أن تنطق إجابتك على لسانك بسهولة. بالإضافة إلى ما هو واضح (لديهم أسماء مختلفة)، فهي تختلف في بنيتها الداخلية. لقد ناقشنا سابقًا البنية الداخلية لكل من ArrayList و LinkedList ، لذا لن أتعمق في تفاصيل التنفيذ الخاصة بهما. سأذكرك فقط أنه يتم تنفيذ ArrayList باستخدام مصفوفة داخلية يزيد حجمها ديناميكيًا وفقًا لهذه الصيغة:
<size of the current array> * 3 / 2 + 1
بالإضافة إلى ذلك، يستخدم تنفيذ LinkedList قائمة داخلية مرتبطة بشكل مزدوج، أي أن كل عنصر له إشارة إلى العناصر السابقة والتالية، باستثناء العناصر الموجودة في بداية القائمة ونهايتها. يحب القائمون على المقابلات طرح هذا السؤال مثل هذا، "أيهما أفضل، ArrayList أم LinkedList ؟" على أمل اللحاق بك. ففي النهاية، إذا قلت أن أحدهما أفضل، فقد أعطيت إجابة خاطئة. استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java.  الجزء 10 - 2بدلاً من ذلك، يجب عليك توضيح الموقف المحدد الذي تتحدث عنه: الوصول إلى العناصر عن طريق الفهرس أو الإدراج في منتصف القائمة. وبعد ذلك، اعتمادًا على إجابتهم، يمكنك توضيح أيهما أفضل. لقد وصفت سابقًا كيفية عمل ArrayList و LinkedList في كل موقف. دعونا نلخص ذلك من خلال وضعها في صف واحد للمقارنة: إضافة عنصر (إضافة)
  1. إذا لم يتم تحديد فهرس، فسيتم إضافة عنصر جديد تلقائيًا إلى النهاية لكلا النوعين من القوائم. في LinkedList ، سيصبح العنصر الجديد هو الذيل الجديد (ستتم إعادة كتابة زوج من المراجع فقط، وبالتالي فإن التعقيد الخوارزمي هو O(1) ).

    تضيف طريقة الإضافة عنصرًا إلى آخر خلية فارغة في المصفوفة ( O(1) ).

  2. إن إضافة عنصر حسب الفهرس يعني عادةً إدراجه في مكان ما في منتصف القائمة. في LinkedList ، ستقوم الطريقة أولاً بالبحث عن الموقع المطلوب عن طريق التكرار فوق العناصر من الذيل والرأس ( O(n/2) ) ثم ستقوم بإدراج القيمة عن طريق الكتابة فوق مراجع العناصر على كلا الجانبين حيث تم إدراج عنصر جديد ( O(1) ). سيكون التعقيد الخوارزمي الإجمالي لهذه العملية هو O(n/2) .

    في نفس الموقف (الإضافة حسب الفهرس)، تبحث ArrayList عن الموقع المطلوب ( O(1) ) ثم تقوم بنقل جميع العناصر الموجودة إلى اليمين (بما في ذلك العنصر المخزن بالفعل في الفهرس المحدد) إلى اليمين بمقدار عنصر واحد (وهو ما قد يتطلب إنشاء مصفوفة داخلية جديدة ونسخ العناصر إليها) ( O(n/2) ). التعقيد العام هو O(n/2) .

  3. إن إضافة عنصر إلى بداية LinkedList يشبه إضافة عنصر إلى النهاية: يصبح العنصر الجديد هو الرأس الجديد ( O(1) ). لكن بالنسبة لقائمة ArrayList، تتطلب هذه العملية نقل جميع العناصر إلى اليمين ( O(n) ).

خلاصة القول هي أنه بالنسبة لقائمة LinkedList ، سيتراوح التعقيد الخوارزمي من O(1) إلى O(n/2) . ملاحظة أخرى هي أنه كلما اقترب الإدراج من نهاية القائمة أو بدايتها، كلما كان الإدراج أسرع. بالنسبة لـ ArrayList ، يتراوح التعقيد الخوارزمي من O(1) إلى O(n) ، وكلما اقترب الإدراج من نهاية القائمة، كلما كان أسرع. ضبط عنصر (مجموعة) تقوم هذه العملية بكتابة عنصر في الموضع المحدد في القائمة، والكتابة فوق أي عنصر موجود. في LinkedList ، تشبه هذه العملية عملية الإضافة، نظرًا لأن التحدي الأكبر هنا هو العثور على موقع العنصر. تتم الكتابة فوق العنصر الموجود عن طريق تحديث زوج من المراجع، لذلك لدينا مرة أخرى تعقيد خوارزمي يختلف من O(1) إلى O(n/2) ، اعتمادًا على مسافة الموضع المطلوب من نهاية القائمة أو بدايتها. لكن بالنسبة إلى ArrayList ، تبحث هذه العملية عن الخلية المطلوبة حسب الفهرس وتكتب العنصر الجديد هناك. مثل عملية المجموعة، فإن البحث حسب الفهرس له تعقيد خوارزمي يبلغ O(1) . الحصول على عنصر حسب الفهرس (الحصول على) يتبع الحصول على عنصر من LinkedList نفس مبدأ البحث المستخدم في العمليات الأخرى. ويعتمد التعقيد على المسافة من النهاية أو البداية، أي أنه يختلف من O(1) إلى O(n/2) . كما ذكرنا سابقًا، بالنسبة إلى ArrayList ، فإن العثور على عنصر حسب الفهرس في المصفوفة الداخلية له درجة تعقيد O(1) . إزالة عنصر حسب الفهرس (إزالة) بالنسبة لـ LinkedList ، ينطبق نفس المبدأ مرة أخرى. أولاً، تم تحديد موقع العنصر، ثم إعادة كتابة المراجع، ويشير الآن جيران العنصر المحذوف إلى بعضهم البعض، مما يؤدي إلى إزالة المراجع إلى العنصر المحذوف، والتي سيتم تنظيفها لاحقًا بواسطة جامع البيانات المهملة. بمعنى آخر، لا يزال التعقيد الخوارزمي كما هو، فهو يختلف من O(1) إلى O(n/2) . بالنسبة لـ ArrayList ، هذه العملية أشبه بإضافة عنصر جديد (إضافة). أولاً، تقوم الطريقة بالعثور على العنصر المطلوب ( O(1) ) وإزالته، ثم يتم إزاحة جميع العناصر الموجودة على اليمين خطوة واحدة إلى اليسار من أجل سد الفجوة الناتجة عن الإزالة. إزالة عنصر لها نفس التعقيد الخوارزمي مثل عملية الإضافة - من O(1) إلى 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 طريقة، وهي بدورها موروثة من قبل جميع الطبقات. استكشاف الأسئلة والأجوبة من مقابلة عمل لوظيفة مطور Java.  الجزء 10 - 3

95. ما هو يساوي () و hashCode () المستخدم في جافا؟

hashCode () هي طريقة لفئة الكائن التي ترثها جميع الفئات. وتتمثل مهمتها في إنشاء رقم يمثل كائنًا محددًا. يمكن العثور على مثال على هذه الطريقة في HashMap ، حيث يتم استدعاؤها على الكائنات الرئيسية للحصول على رمز التجزئة المحلي، والذي سيحدد المجموعة (خلية المصفوفة الداخلية) التي سيتم تخزين زوج القيمة الرئيسية فيها. تُستخدم هذه الطريقة بشكل عام في طريقة يساوي () كإحدى طرقها الرئيسية لتحديد الكائنات. يساوي () هي طريقة لفئة الكائن التي تتمثل مهمتها في مقارنة الكائنات وتحديد ما إذا كانت متساوية. يتم استخدام هذه الطريقة في كل مكان نحتاج فيه إلى مقارنة الكائنات، لأن معامل المقارنة القياسي == غير مناسب للكائنات، لأنه يقارن فقط مراجع الكائنات.

96. أخبرنا عن العقد بين يساوي () و hashCode () في جافا؟

أولاً، اسمحوا لي أن أقول أنه لكي تعمل أساليب يساوي () و hashCode () بشكل صحيح، يجب أن يتم تجاوزها بشكل صحيح. يجب أن تتبع تطبيقاتها الجديدة هذه القواعد:
  • يجب أن تحتوي الكائنات المتطابقة التي تُرجع تساويها بشكل صحيح على نفس رموز التجزئة.
  • الكائنات التي لها نفس رموز التجزئة ليست بالضرورة متساوية.
يبدو الآن مكانًا جيدًا للتوقف مؤقتًا حتى الجزء التالي من المراجعة!
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION