هياكل البيانات في جاوة. أساليب الطبقة المساعد مفيدة

مرحبا هابر!

أنا مهندس برمجيات في EPAM. منذ أكثر من 8 سنوات ، كنت أعمل مع الكود القديم المكتوب بلغة جافا (مع توقع التعليقات ، لاحظت أن الفهم والتسامح للتراث بدأ قبل فترة طويلة من EPAM ، وفي الختام ستجد الإجابة لماذا). في كثير من الأحيان في العمل واجهت نفس العيوب المتكررة. هذا دفعني إلى كتابة ملاحظة ، وأريد أن أبدأ بهياكل البيانات وفئات المساعدة مجموعات ومصفوفات . لسبب ما ، يهمل بعض المطورين استخدامها ، ولكن دون جدوى

غالبًا ما يتعين على مطور Java التعامل مع هياكل البيانات المختلفة. يمكن أن يكون صفائف ، وجميع أنواع المجموعات أو تطبيقات Map . يبدو أن كل شيء معهم واضح ومفهوم ، ولكن هناك العديد من الأشياء الصغيرة التي يسهل التعثر عليها.

قد تكون هذه الملاحظة مفيدة لكلا المبتدئين الذين لا يعرفون هذه الفروق الدقيقة والمطورين ذوي الخبرة الذين قد ينسون بعضًا من هذا.

صورة
الصورة بواسطة ammiel jr على Unsplash

CAT


أريد أن أبدي تحفظًا على الفور بأن هذه المادة مناسبة لجافا 8. من الواضح أن بعض الأشياء قد تم تنفيذها بالفعل بشكل أفضل في Java 9+ ، ولكن معظم المشروعات الكبيرة تستخدم غالبًا إصدار Java 8 (وأحيانًا جافا 6).

ما هي أفضل طريقة للحصول على مجموعة تستند إلى مجموعة؟


أقترح البدء بتكوين مجموعة قائمة على مجموعة.

في معظم الأحيان ، تحدث هذه الطريقة:

Integer[] someArray = {9, 10, 11, 12}; List<Integer> list = Arrays.asList(someArray); 

إنه بالتأكيد يعمل ، لكن هل كل شيء على ما يرام؟ وهل هناك أي حلول بديلة؟

اثنين من السلبيات لهذا النهج تتبادر إلى الذهن في وقت واحد:

  • أولاً ، الأسلوب Arrays.asList بإرجاع قائمة . ولكن ماذا لو كنا بحاجة إلى تطبيق آخر لمجموعة؟ Arrays.asList لن يسمح بذلك ، ولكن سيتم النظر في بديل آخر.
  • ثانياً ، القائمة التي تم الحصول عليها عن طريق استدعاء Arrays.asList لا تدعم تغيير الحجم. أعتقد أن الكثيرين توصلوا إلى استثناء ناشئ عن العمل مع هذه القائمة.

في واجهة المجموعات ، يمكنك العثور على بديل لطريقة Arrays.asList - طريقة Collections.addAll . إليك كيفية استخدامه:

 //      (List, Set, ...) Collection<Integer> collection = ...; Integer[] someArray = {9, 10, 8, 7}; Collections.addAll(collection, someArray); 

أو ببساطة:

 Collections.addAll(collection, 11, 12, 13, 14); 

يقبل أسلوب Collections.addAll كائن مجموعة وصفيف في الإدخال. بدلاً من صفيف ، يمكنك أيضًا تحديد عناصر مفصولة بفواصل.

ما هي فوائد Collections.addAll على Arrays.asList ؟

  • بادئ ذي بدء ، عند إنشاء مجموعات تستند إلى صفيف Collections.addAll ، فإنها تعمل بشكل أسرع بكثير من طريقة addAll للمجموعة مع إدخال Arrays.asList . يمكن العثور على هذا في JavaDoc من هذه الطريقة:
    يشبه سلوك طريقة الراحة هذه سلوك c.addAll (Arrays.asList (elements)) ، ولكن من المحتمل أن يتم تشغيل هذه الطريقة بشكل أسرع في معظم الحالات
  • بالإضافة إلى ذلك ، لا يعمل Collections.addAll مع List فقط ، ولكن مع أي مجموعة أخرى.
  • وعند استخدام هذه الطريقة ، لا توجد مشكلة في تغيير الحجم.

ما هي أسهل طريقة لطباعة صفيف أو صفيف متعدد الأبعاد أو مجموعة؟


دعنا ننتقل إلى مسألة الحصول على تمثيل مطبوع لمجموعة أو مجموعات.

إذا قمنا بإنشاء System.out.println (someArray) ، فسوف نحصل على شيء مثل هذا:
[Ljava.lang.Integer؛ @ 6d06d69c.
يتوقع نتيجة مماثلة عند استخدام الأسلوب toString () على صفيف.
لإخراج الصفيف ، تأتي طريقة Arrays.toString (...) في عملية الإنقاذ .

 Integer[] someArray = new Integer[]{1, 2, 3}; System.out.println(Arrays.toString(someArray)); 

سيكون الإخراج لهذا الخط:

 [1 ، 2 ، 3] 

إذا كنا نتحدث عن مجموعة متعددة الأبعاد ، فيمكنك استخدام الطريقة: Arrays.deeptoString .

 int[][] a = { {1, 2, 3}, {4, 5, 6} }; System.out.println(Arrays.deepToString(a)); 

سيكون إخراج هذا المقتطف كما يلي:

  [[1 ، 2 ، 3] ، [4 ، 5 ، 6]] 


وبالتالي ، ليس من الضروري فرز المصفوفة عبر أي حلقة يدوياً لعرض عناصرها ؛ يكفي استخدام هذه الطريقة.

بالنسبة للمجموعات أو تطبيقات Map ، لا توجد مشاكل. عادةً ما يتم إخراج كافة بنيات البيانات باستثناء الصفيف.

افترض أن هناك مثال:

 Collection<Integer> someCollection = new HashSet<>(); someCollection.add(1); someCollection.add(2); System.out.println(someCollection); Map<Integer, String> someMap = new HashMap<>(); someMap.put(1, "Some 1"); someMap.put(2, "Some 2"); System.out.println(someMap); 

لاحظ في الإخراج أدناه أنه تم عرض كل من المجموعة والخريطة في تنسيق سهل القراءة:

 [1 ، 2] 
 {1 = بعض 1 ، 2 = بعض 2} 


ما مدى سهولة مقارنة المصفوفات مع بعضها البعض؟


هناك حالات عندما تحتاج إلى مقارنة المصفوفات. هناك طريقة في فئة المصفوفات تسمح بمثل هذه المقارنة. يقارن الأسلوب Arrays.equals عدد العناصر ويتحقق من معادلة العناصر المقابلة.

دعنا نقول لدينا فئة عناصر مع حقل واحد وبعض المعادلات

 private class Element { final String name; private Element(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Element element = (Element) o; return Objects.equals(name, element.name); } @Override public int hashCode() { return Objects.hash(name); } } 

تحديد ثلاث صفائف:

 Element[] firstElementArray = { new Element("a"), new Element("b"), new Element("c") }; Element[] secondElementArray = {new Element("c"), new Element("b"), new Element("a") }; Element[] thirdElementArray = { new Element("a"), new Element("b"), new Element("c") }; 

لاحظ أن الصفيفين الأول والثالث تحتويان على عناصر بنفس الترتيب.
الآن يمكنك إجراء المقارنة باستخدام طريقة Arrays.equals .

 System.out.println("first equals to second? " + Arrays.equals(firstElementArray, secondElementArray)); System.out.println("second equals to third? " + Arrays.equals(secondElementArray, thirdElementArray)); System.out.println("first equals to third? " + Arrays.equals(firstElementArray, thirdElementArray)); 

ستكون النتيجة على النحو التالي:

 أول يساوي الثانية؟  زائف 
 الثانية تساوي الثالثة؟  زائف 
 أول يساوي الثالثة؟  صحيح 


كيفية نسخ مجموعة بكفاءة


في كثير من الأحيان يمكنك أن ترى في الكود يدويا نسخ المصفوفات باستخدام الحلقات. ومع ذلك ، هناك طريقة System.arraycopy التي سيتم نسخ أسرع بكثير.

أقترح نظرة على هذا المثال البسيط:

 Element[] elements = { new Element("a"), new Element("b"), new Element("c") }; Element[] copyOfElements = new Element[elements.length]; System.arraycopy(elements, 0, copyOfElements, 0, elements.length); System.out.println(Arrays.toString(copyOfElements)); 

لدينا مجموعة من العناصر. نقوم بإنشاء مجموعة فارغة من نفس الطول ونسخ جميع العناصر من الأول إلى الثاني. نتيجة لذلك ، حصلنا على الاستنتاج التالي:

 [العنصر {name = 'a'} ، العنصر {name = 'b'} ، العنصر {name = 'c'}] 


كيفية فرز مجموعة أو مجموعة بطرق مختلفة؟


يمكن فرز المصفوفات باستخدام طريقة Arrays.sort (someArray) . إذا كنت ترغب في فرز الصفيف بترتيب عكسي ، يمكنك تمرير Collections.reverseOrder () كمعلمة ثانية إلى الإدخال في هذه الطريقة.

على سبيل المثال ، هناك صفيف نفرزه مباشرة ثم بترتيب عكسي:

 String[] someArray = new String[]{"b", "a", "c"}; Arrays.sort(someArray); System.out.println(Arrays.toString(someArray)); Arrays.sort(someArray, Collections.reverseOrder()); System.out.println(Arrays.toString(someArray)); 

الاستنتاج سيكون على النحو التالي:

 [أ ، ب ، ج] 
 [ج ، ب ، أ] 


بالإضافة إلى الفرز المباشر والعكس ، يحدث أحيانًا أنك تحتاج إلى فرز مجموعة من السلاسل بغض النظر عن الحالة. من السهل القيام بذلك عن طريق تمرير String.CASE_INSENSITIVE_ORDER كمعلمة ثانية إلى Arrays.sort .

Collections.sort ، لسوء الحظ ، يسمح فقط بترتيب قائمة التطبيقات.

ما الخوارزمية فرز جافا؟


آخر ما يجب ذكره عند الحديث عن الفرز في Java هو أنه في Java ، يتم استخدام "فرز بسيط" لأنواع أبسط ، ويستخدم "دمج مستقر" للكائنات. لذلك لا ينبغي أن تنفق الموارد على تطوير تطبيقك الخاص لطريقة الفرز حتى يظهر منشئ ملفات التعريف أنه ضروري.

ماذا لو كان لدينا مجموعة وطريقة تقبل Iterable؟


أقترح الآن الانتقال إلى سؤال مثل تمرير صفيف إلى طريقة تتطلب Iterable . اسمحوا لي أن أذكرك بأن Iterable هي واجهة تحتوي على طريقة التكرار () ، والتي يجب على Iterator إرجاعها.

إذا كانت هناك طريقة تقبل Iterable عند الإدخال ، فلا يمكن نقل الصفيف إلى هذا الحد تمامًا. على الرغم من أنه يمكنك التكرار عبر صفيف في حلقة for ، إلا أنه غير قابل للتكرار .

 String[] someArray = new String[]{"a", "b", "c"}; for (String currentString : someArray) { ... } 

في هذا المثال ، كل شيء على ما يرام. ولكن إذا كان هناك طريقة:

 private static void someIteration(Iterable<String> iterable) { ... } 

لن يتم تجميع هذا الخط:

 someIteration(someArray); 

المخرج الوحيد في هذه الحالة هو تحويل الصفيف إلى مجموعة وإطعامها بالفعل إلى مثل هذه الطريقة.

باختصار عن بعض طرق المجموعات المفيدة


طريقةتعليق
الحد الأقصى (المجموعة) والحد الأقصى (المجموعة ، المقارنة)
دقيقة (مجموعة) ودقيقة (مجموعة ، مقارنة)
يرجى ملاحظة أنه يمكنك تقديم طلب إلى المدخلات من المقارنة
indexOfSubList (قائمة ، قائمة)
يبحث عن فهرس التواجد الأول لقائمة واحدة (الوسيطة الثانية) في قائمة أخرى (الوسيطة الأولى)
lastIndexOfSubList (قائمة ، قائمة)
يبحث عن فهرس التواجد الأخير لقائمة واحدة (الوسيطة الثانية) في قائمة أخرى (الوسيطة الأولى)
عكس (قائمة)
إعادة ترتيب العناصر بترتيب عكسي

ما يستحق القراءة؟


هذا ليس سوى جزء صغير من الأدوات التي يمكن أن تجعل الحياة أسهل للمطور عند العمل مع هياكل البيانات. يمكن العثور على العديد من النقاط المثيرة للاهتمام في عمل المجموعات نفسها وأدوات ملائمة للعمل معهم في كتاب Bruce Eckel "Java Philosophy" (الإصدار الكامل الرابع). ومع ذلك ، يجب أن تكون حذرًا ، لأنه يواجه مواقف لم يعد من الممكن تشغيلها على Java 7 و Java 8 أو أعلى. على الرغم من أن Java 6 موصوفة في هذا الكتاب ، إلا أن مواده تظل ذات صلة إلى حد كبير اليوم.

بالطبع ، يجب ألا تكون "فلسفة جافا" محدودة. قراءة أي من هذه الكتب لن تؤذي أي مطور جافا:

  • «جافا. البرمجة الفعالة ، "جوشوا بلوخ.
  • "إعادة بيع ديون. تحسين تصميم الشفرة الحالية ، "مارتن فاولر.
  • "رمز نظيف. الخلق والتحليل وإعادة البناء "روبرت مارتن.
  • ربيع 5 للمحترفين ، جوليان كوزمين وغيرها.
  • "تطوير جافا يحركه الاختبار" ، فيكتور فارسيك ، أليكس غارسيا (لم يصدر بعد باللغة الروسية).

ما هي النتيجة؟


إذا توصلت إلى أفكار مثيرة للاهتمام يمكن أن تكمل ما كتب في هذه المقالة ، شاركها في التعليقات.

أود أيضًا أن أتمنى حظًا موفقًا وصبرًا لأولئك الذين يعملون مع الكود القديم القديم. معظم المشاريع الكبرى قديمة. وأهميتها بالنسبة للعملاء يصعب المبالغة في تقديرها. والشعور بالنصر من القضاء على هذا الخطأ ، الذي استغرق أكثر من أسبوع للعثور على الأسباب ، ليس أدنى من المشاعر في نهاية تنفيذ ميزة جديدة.

شكرا لاهتمامكم سأكون سعيدًا إذا كان أي من المقدمين سيكون مفيدًا.
كل النجاح!

Source: https://habr.com/ru/post/ar476098/


All Articles