المقال عبارة عن ترجمة للمقال " أهم 10 أسئلة حول مجموعات Java " . فيما يلي الأسئلة الأكثر شيوعًا حول المجموعات في Java، والتي تم طرحها ومناقشتها في Stackowerflow. قبل أن تنظر إلى هذه الأسئلة، سيكون من الجيد إلقاء نظرة على مخطط التسلسل الهرمي للفصل. 1. متى يجب استخدام LinkedList بدلاً من ArrayList؟ ArrayList هي في الواقع مصفوفة، ويمكن الوصول إلى عناصرها مباشرة عن طريق الفهرس. إذا فاضت المصفوفة، يصبح من الضروري إنشاء مصفوفة جديدة بمساحة أكبر. سيستغرق وضع جميع العناصر ونقلها وقتًا O(n). من الضروري أيضًا إضافة العناصر وإزالتها لنقل العناصر الموجودة في المصفوفة. ربما يكون هذا هو أكبر إزعاج لاستخدام ArrayList. LinkedList عبارة عن قائمة مزدوجة من روابط العناصر. وبالتالي، للوصول إلى العنصر الموجود في المركز، عليك البحث من البداية إلى نهاية الورقة. من ناحية أخرى، تعد إضافة عنصر وإزالته في قائمة مرتبطة أسرع لأن هذه العمليات تغير القائمة نفسها فقط. تتم مقارنة أسوأ الأوقات أدناه:
طريقة
قائمة المصفوفات
قائمة مرتبطة
الحصول على (الفهرس)
يا(1)
على)
إضافة (ه)
على)
يا(1)
إضافة (ه، فهرس)
على)
على)
إزالة (الفهرس)
على)
على)
التكرار.إزالة ()
على)
يا(1)
مكرر.إضافة (E)
على)
يا(1)
على الرغم من وقت التشغيل، يجب مراعاة استخدام الذاكرة بشكل فردي للقوائم الكبيرة. في LinkedList، يجب أن تحتوي كل عقدة على مؤشرين إضافيين على الأقل لربط العقد السابقة والتالية، بينما في ArrayList، هناك حاجة إلى مصفوفة من العناصر فقط. المزيد من المقارنات بين
قوائم ArrayList وLinkedList وVector .
2. المكافئ الفعال لإزالة العناصر أثناء تكرار المجموعة الطريقة الصحيحة الوحيدة لتعديل (إزالة العناصر) مجموعة أثناء التكرار هي استخدام
Iterator.remove() . على سبيل المثال: الخطأ الأكثر شيوعًا هو: سوف تحصل على
ConcurrentModificationException أثناء تشغيل الكود أعلاه. يحدث هذا لأنه تم إنشاء المكرّر للتنقل عبر القائمة بأكملها، ولكن في نفس الوقت يتم تغيير الورقة عن طريق استدعاء Iterator.remove(). كما هو مكتوب في الوثائق لهذا الاستثناء،
Iterator
itr = list.iterator(); while(itr.hasNext()) { // do something itr.remove(); }
for(Integer i: list) { list.remove(i); }
"لا يجوز عمومًا أن يقوم أحد الخيوط بتعديل مجموعة بينما يتكرر مؤشر ترابط آخر فوقها."
بشكل عام، من غير المقبول أن يقوم مؤشر ترابط واحد بتعديل مجموعة أثناء مرور مؤشر ترابط آخر بها.
3. كيفية تحويل القائمة إلى صفيف int[]؟ أسهل طريقة للقيام بذلك هي استخدام
ArrayUtils ، الموجود في مكتبة
Apache Commons Lang .
int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0])); لا يوجد اختصار لهذا التعبير في JDK. تذكر أنه لا يمكنك استخدام List.toArray() لأن هذا التعبير يحول القائمة إلى Integer[] (وهو
ليس نوعًا بدائيًا). ستكون الطريقة الصحيحة هي الخيار التالي:
int[] array = new int[list.size()]; for(int i=0; i < list.size(); i++) { array[i] = list.get(i); }4. كيفية تحويل مصفوفة int[] إلى قائمة؟ الطريقة الأسهل أيضًا هي استخدام
ArrayUtils في مكتبة
Apache Commons Lang ، كما هو مذكور أعلاه.
List list = Arrays.asList(ArrayUtils.toObject(array)); كما أنه لا يوجد اختصار لهذا التعبير في JDK.
5. ما هي أفضل طريقة لتصفية المجموعة؟ يمكنك استخدام حزم الجهات الخارجية مثل
Guava أو
Apache Commons Lang لزيادة الوظائف. تحتوي كلتا الحزمتين على طريقة filter() (في فئة
Collections2 من Guava و
CollectionUtils من Apache). سوف تقوم طريقة التصفية () بإرجاع العناصر التي تطابق المسند المحدد. في JDK، كل شيء أكثر تعقيدًا. والخبر السار هو أنه ستتم إضافة المسندات في Java 8
، ولكن في الوقت الحالي تحتاج إلى استخدام Iterator للتكرار عبر المجموعة بأكملها. بالطبع، يمكنك تقليد المسار الذي يتبعه Guava وApache من خلال التعرف على واجهة Predicate الجديدة. يمكننا الآن استخدام الكود التالي لتصفية المجموعة:
6. كيف يمكن تحويل القائمة إلى مجموعة بسهولة؟ هناك طريقتان للقيام بذلك، اعتمادًا على الطريقة التي تريد بها تعريف المساواة. الجزء الأول من التعليمات البرمجية يضع القائمة في HashSet. يتم تحديد التكرار في هذه الحالة بشكل أساسي بواسطة hashCode(). عادة هذا سوف يعمل. ولكن إذا كنت بحاجة إلى مراعاة مسار المقارنة، فسيكون من الأفضل استخدام الجزء الثاني من الكود، حيث يمكنك تحديد المقارنة الخاصة بك.
7. كيف يمكنني إزالة العناصر المكررة من ArrayList؟ هذا السؤال مرتبط إلى حد ما بالسؤال أعلاه. إذا كان ترتيب العناصر في ArrayList لا يهمك، فستكون الخطوة الذكية هي وضع الورقة في مجموعة لإزالة التكرارات، ثم إعادتها مرة أخرى إلى القائمة. أدناه مثال. إذا كان ترتيب العناصر مهمًا بالنسبة لك، فيمكن ضمان الترتيب عن طريق وضع القائمة في
LinkedHashSet ، الموجود في JDK القياسي.
8. جمع مرتبةint[] array = {1,2,3,4,5}; List
list = new ArrayList
(); for(int i: array) { list.add(i); }
Iterator
itr = list.iterator(); while(itr.hasNext()) { int i = itr.next(); if (i > 5) { // filter all ints bigger than 5 itr.remove(); } }
public interface Predicate
{ boolean test(T o); } public static
void filter(Collection
collection, Predicate
predicate) { if ((collection != null) && (predicate != null)) { Iterator
itr = collection.iterator(); while(itr.hasNext()) { T obj = itr.next(); if (!predicate.test(obj)) { itr.remove(); } } } }
filter(list, new Predicate
() { public boolean test(Integer i) { return i <= 5; } });
Set
set = new HashSet
(list);
Set
set = new TreeSet
(aComparator); set.addAll(list);
ArrayList** list = ... // initial a list with duplicate elements Set
set = new HashSet
(list); list.clear(); list.addAll(set);
هناك عدة طرق لدعم مجموعة مفروزة في Java. توفر جميعها مجموعة بالترتيب الطبيعي أو بواسطة مقارن محدد. في حالة الترتيب الطبيعي، تحتاج أيضًا إلى تنفيذ الواجهة
القابلة للمقارنة على العنصر.
يمكن لـ Collections.sort() فرز القائمة. كما هو مذكور في وثائق Java، هذا النوع مستقر ويضمن أداء n log(n).
توفر PriorityQueue قائمة انتظار منظمة. الفرق بين PriorityQueue و Collections.sort() هو أن PriorityQueue تحافظ على ترتيب قائمة الانتظار طوال الوقت، ولكن يمكنك فقط الحصول على العنصر الأول من قائمة الانتظار. لا يمكنك الوصول بشكل عشوائي إلى عنصر مثل PriorityQueue.get(4).
إذا لم يكن هناك أي تكرارات في المجموعة، فيمكنك تحديد TreeSet . كما هو الحال أيضًا مع PriorityQueue، تحافظ TreeSet على مجموعة مرتبة في جميع الأوقات. يمكنك الحصول على أصغر أو أكبر عنصر من TreeSet، لكن لا يزال يتعذر عليك الوصول العشوائي إلى العناصر.
ببساطة، توفر Collections.sort() قائمة مرتبة لمرة واحدة. تحتفظ PriorityQueue وTreeSet بمجموعة مرتبة في جميع الأوقات، على حساب عدم الوصول المفهرس إلى العناصر.
9. Collections.emptyList() أو مثيل جديد ينطبق نفس السؤال علىemptyMap() وemptySet(). تُرجع كلتا الطريقتين قائمة فارغة، لكن Collections.emptyList() هي قائمة غير قابلة للتغيير. وهذا يعني أنه
لا يمكنك إضافة عناصر جديدة إلى القائمة "الفارغة". في الخلفية، كل استدعاء للأسلوب Collections.emptyList() لا يؤدي فعليًا إلى إنشاء مثيل جديد للقائمة الفارغة. بدلاً من ذلك، سيتم إعادة استخدام المثيل الفارغ الموجود بالفعل. إذا كنت على دراية بـ Singleton كنمط
تصميم ، فيجب أن تفهم المقصود. من المفترض أن يمنحك هذا أداءً
أفضل إذا تم الاتصال به بشكل متكرر.
10 نسخ مجموعة، Collections.copy() هناك طريقتان لنسخ قائمة المصدر إلى قائمة الوجهة. إحدى الطرق هي استخدام مُنشئ ArrayList. هناك طريقة أخرى وهي استخدام طريقة
Collections.copy() . لاحظ في السطر الأول: نحن نخصص قائمة بنفس طول القائمة الأصلية على الأقل، لأن وثائق Java حول المجموعات تقول:
ArrayList
dstList = new ArrayList
(srcList);
يجب أن تكون قائمة الوجهة على الأقل بنفس طول قائمة المصدر.
مما يعني أن القائمة النهائية يجب ألا تكون أقصر من القائمة الأصلية. كلتا الطريقتين نسخ سطحي. إذن ما الفرق بين هاتين الطريقتين؟ أولاً، لن تقوم Collections.copy() بإعادة تخصيص سعة مجموعة dstList، حتى إذا لم يكن لدى dstList مساحة كافية لاحتواء جميع العناصر من srcList. بدلاً من ذلك، سيتم طرح
IndexOutOfBoundsException . قد يتساءل المرء عما إذا كان هناك أي فائدة لهذا. والسبب هو أن هذا يضمن تشغيل الطريقة بشكل خطي في الوقت المناسب. يعد هذا مناسبًا أيضًا عندما تريد إعادة استخدام المصفوفات بدلاً من إعادة تخصيص الذاكرة في مُنشئ ArrayList.
بدلاً من الاستنتاج، إذا كان لا يزال لديك أسئلة بعد قراءة المقال، فلا تتردد في طرحها في التعليقات. وأيضاً إذا وجدت أي خطأ في الترجمة أو أي خطأ آخر فاكتب لرئيس الوزراء وسيتم تصحيحه ولكم الشكر.
إبداعي.ArrayList
dstList = new ArrayList
(srcList.size()); Collections.copy(dstList, srcList);
GO TO FULL VERSION