JavaRush /مدونة جافا /Random-AR /تحليل الأسئلة والأجوبة من المقابلات لمطور جافا. الجزء 10

تحليل الأسئلة والأجوبة من المقابلات لمطور جافا. الجزء 10

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

89. كيف يختلف ArrayList عن LinkedList؟

يعد هذا أحد الأسئلة الأكثر شيوعًا إلى جانب السؤال المتعلق بالبنية الداخلية لـ HashMap . لا تكتمل أي مقابلة بدونها، وبالتالي فإن الإجابة عليها يجب أن "ترتد عن أسنانك". بالإضافة إلى الأسماء الواضحة - المختلفة - فهي تختلف في البنية الداخلية. لقد قمنا سابقًا بفحص البنية الداخلية لكل من ArrayList و LinkedList ، لذلك لن أخوض في تفاصيل تنفيذهما. اسمحوا لي فقط أن أذكركم أنه يتم تنفيذ ArrayList بناءً على مصفوفة داخلية، والتي يتم زيادتها حسب الحاجة وفقًا للصيغة:
<размерТекущегоМассива> * 3 / 2  + 1
في الوقت نفسه، يتم تنفيذ LinkedList بناءً على قائمة داخلية مرتبطة بشكل مزدوج، أي أن كل عنصر له رابط إلى السابق والذي يليه، باستثناء القيم التي تمثل بداية/نهاية القائمة. يحب الأشخاص طرح هذا السؤال بالتنسيق: "أيهما أفضل - ArrayList أم LinkedList ؟"، على أمل أن يلفت انتباهك. ففي النهاية، إذا أشرت إلى إحداها كإجابة، فستكون الإجابة خاطئة. تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 10 - 2بدلاً من ذلك، يجب عليك توضيح الموقف المحدد الذي تتحدث عنه - الوصول إلى الفهرس أو الإدراج في منتصف القائمة. اعتمادا على الإجابة، سوف تكون قادرا على شرح اختيارك. لقد وصفت سابقًا كيفية عمل ArrayList و LinkedList في موقف أو آخر. دعونا نلخص ذلك بوضعهم في نفس الصفحة للمقارنة: إضافة عنصر (إضافة)
  1. Добавление нового element без указания индекса How местоположения будет происходить автоматически в конец обоих списков. В LinkedList новый элемент станет новым хвостом (происходит только перезаписывание пары ссылок — алгоритмическая сложность O(1)).

    В ArrayList будет добавлен новый элемент в последнюю пустую ячейку массива — O(1).

  2. Добавление element по индексу How правило подразумевает вставку примерно в середину списка. В LinkedList сперва будет вестись поиск нужного места с помощью перебора элементов с “хвоста” и “головы” — O(n/2), а после — вставка значения путем переопределения ссылок элементов, между которыми вставляется новый — O(1). Суммарная алгоритмическая сложность данного действия будет O(n/2).

    ArrayList в данной ситуации по индексу находит элемент — O(1), и все элементы справа (включая элемент, который уже хранится по данному индексу) двигаются на одну единицу вправо (при этом возможно понадобится создание нового списка и копирование элементов в него) — O(n/2). Суммарная сложность — O(n/2).

  3. Добавление element в начало списка в 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) . أخذ عنصر حسب الفهرس (get) في 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، يكون الوصول ممكنًا عن طريق فهرس العناصر: تحتوي عملية الحصول على تعقيد خوارزمي لـ O(1) ، وفي HashSet لا يمكن الحصول على العنصر المطلوب إلا بالقوة الغاشمة، وهذا من O(1) إلى O(n) ;

  • يسمح ArrayList بالعناصر المكررة. في HashSet، جميع العناصر فريدة من نوعها: لن تعمل إضافة عنصر إلى HashSet الذي يوجد نظيره بالفعل في المجموعة (يتم التحقق من التكرارات باستخدام رمز التجزئة، ومن هنا جاء اسم هذه المجموعة)؛

  • يتم تنفيذ ArrayList باستخدام مصفوفة داخلية، ويتم تنفيذ HashSet باستخدام HashMap داخلي ؛

  • تحافظ ArrayList على ترتيب إدراج العناصر، في حين أن HashSet عبارة عن مجموعة غير مرتبة ولا تحافظ على ترتيب العناصر؛

  • يسمح ArrayList بأي عدد من القيم الفارغة (خالية)، ويمكن إدراج قيمة فارغة واحدة فقط في HashSet (بعد كل شيء، تفرد العناصر).

91. لماذا يوجد هذا التنوع في تطبيقات المصفوفة الديناميكية في Java؟

تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 10 - 3حسنًا، هذا سؤال فلسفي أكثر. حسنًا، لماذا يأتون بالعديد من التقنيات الجديدة المختلفة؟ للراحة. في الواقع، الأمر نفسه ينطبق على عدد كبير من تطبيقات المصفوفات الديناميكية. لا يمكن تسمية أي منهم بالأفضل أو المثالي. ولكل منها ميزة في بعض المواقف المحددة. ومهمتنا هي معرفة الاختلافات بينهم ونقاط القوة والضعف لديهم: حتى نتمكن من استخدام الأنسب في الموقف المناسب.

92. لماذا يوجد هذا التنوع في تطبيقات تخزين القيمة الأساسية في Java؟

الوضع هنا هو نفسه كما هو الحال مع تطبيقات المصفوفة الديناميكية. لا يوجد أحد أفضل: لكل منهم نقاط قوة ونقاط ضعف. وعلينا بالطبع أن نستفيد إلى أقصى حد من نقاط قوتنا. مثال: الحزمة المتزامنة، التي تحتوي على العديد من التقنيات متعددة الخيوط، لها مجموعاتها المتزامنة الخاصة بها . يتمتع نفس ConcurrentHashMap بميزة أمان العمل متعدد الخيوط مع البيانات مقارنةً بـ HashMap العادي ، ولكنه يفقد السرعة في بيئة غير متعددة الخيوط. حسنًا، يتم التوقف تدريجيًا عن استخدام التطبيقات التي ليست الأقوى في أي موقف. مثال: كان المقصود من Hashtable في الأصل أن يكون HashMap آمنًا لمؤشر الترابط ، لكن ConcurrentHashMap تفوق عليه في بيئة متعددة الخيوط وتم نسيان Hashtable في النهاية ولم يعد مستخدمًا.

93. كيفية فرز مجموعة من العناصر؟

أول شيء يجب قوله هو أن فئة عنصر المجموعة يجب أن تنفذ الواجهة القابلة للمقارنة وطريقة المقارنة الخاصة بها . أو تحتاج إلى فئة تقوم بتطبيق Comaprator باستخدام أسلوب المقارنة الخاص بها . يمكنك قراءة المزيد عنهم في هذا المنشور . تحدد كلتا الطريقتين كيفية مقارنة الكائنات من نوع معين. عند الفرز، يعد هذا أمرًا بالغ الأهمية، لأنك تحتاج إلى فهم المبدأ الذي يمكن من خلاله مقارنة العناصر. الطريقة الرئيسية للقيام بذلك هي تنفيذ Comparable ، والذي يتم تنفيذه مباشرة في الفصل الذي تريد فرزه. وفي الوقت نفسه، يعد استخدام المقارنة أقل شيوعًا. لنفترض أنك تستخدم فصلًا دراسيًا من إحدى المكتبات التي لا تحتوي على تطبيق قابل للمقارنة ، ولكنك ستحتاج إلى فرزه بطريقة ما. دون أن تكون قادرًا على تغيير كود هذه الفئة (إلا من خلال توسيعها)، يمكنك كتابة تطبيق Comparator ، حيث تشير إلى المبدأ الذي تريد مقارنة كائنات هذه الفئة به. ومثال آخر. لنفترض أنك تحتاج إلى مبادئ مختلفة لفرز الكائنات من نفس النوع، لذلك تكتب عدة مقارنات تستخدمها في مواقف مختلفة. كقاعدة عامة، تقوم العديد من الفئات الجاهزة بالفعل بتنفيذ الواجهة القابلة للمقارنة - وهي نفس السلسلة . في الواقع، عند استخدامها، لا داعي للقلق بشأن كيفية مقارنتها. أنت فقط تأخذهم وتستخدمهم. الطريقة الأولى والأكثر وضوحًا هي استخدام مجموعة من النوع TreeSet أو TreeMap ، التي تخزن العناصر بترتيب فرزها بالفعل وفقًا لمقارنة فئة العنصر. تذكر أن TreeMap يقوم بفرز المفاتيح، وليس القيم. إذا كنت تستخدم تطبيق Comparator بدلاً من Comparable ، فستحتاج إلى تمرير كائنه إلى مُنشئ المجموعة عند الإنشاء:
TreeSet treeSet = new TreeSet(customComparator);
ولكن ماذا لو كان لديك نوع مختلف من المجموعة؟ كيفية فرز ذلك؟ في هذه الحالة، الطريقة الثانية لفئة الأداة المساعدة للمجموعات مناسبة - طريقة الفرز () . إنها ثابتة، لذا كل ما تحتاجه هو اسم الفئة والطريقة التي يتم تمرير القائمة المطلوبة إليها. على سبيل المثال:
Collections.sort(someList);
إذا كنت لا تستخدم Comparable ، بل تستخدم تطبيقًا للمقارنة ، فستحتاج إلى تمريره كمعلمة ثانية:
Collections.sort(someList, customComparator);
ونتيجة لذلك، سيتغير الترتيب الداخلي لعناصر القائمة التي تم تمريرها: سيتم فرزها وفقًا لمقارن العناصر. وألاحظ أن قائمة العناصر المنقولة يجب أن تكون قابلة للتغيير، أي. قابل للتغيير، وإلا فلن تعمل الطريقة وسيتم طرح UnsupportedOperationException . كطريقة ثالثة ، يمكنك استخدام عملية فرز الدفق ، والتي تقوم بفرز عناصر المجموعة إذا تم استخدام تطبيق Comparable :
someList = someList.stream().sorted().collect(Collectors.toList());
إذا المقارنة :
someList = someList.stream().sorted(customComparator).collect(Collectors.toList());
يمكنك قراءة المزيد عن Stream في هذه المقالة . الطريقة الرابعة هي تنفيذ الفرز يدويًا، مثل الفرز الفقاعي أو الفرز بالدمج .

ClassObject. يساوي وHashCode

94. قدم وصفًا موجزًا ​​لكائن الفئة في Java

في الجزء الثاني من التحليل، تحدثنا بالفعل عن أساليب فئة الكائن ، وسأذكرك أن فئة الكائن هي سلف جميع الفئات في جافا. لديها 11 طريقة، والتي، وفقا لذلك، ترثها جميع الفئات. تحليل الأسئلة والأجوبة من المقابلات لمطور جافا.  الجزء 10 - 4يمكن العثور على معلومات حول جميع الطرق الـ 11 في الجزء الثاني من المناقشة.

95. ما هي استخدامات Equals وHashCode في Java؟

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

96. أخبرنا عن العقد المبرم بين Equals وHashCode في Java؟

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