CodeGym /مدونة جافا /Random-AR /حذف عنصر من ArrayList
John Squirrels
مستوى
San Francisco

حذف عنصر من ArrayList

نشرت في المجموعة
أهلاً! في الدرس الأخير، تعرفنا على الفصل ArrayList، وتعلمنا كيفية إجراء العمليات الأكثر شيوعًا مع هذا الفصل. بالإضافة إلى ذلك، أشرنا إلى عدة اختلافات بين ArrayListالمصفوفة العادية والمصفوفة العادية. لكننا تجاوزنا موضوعًا واحدًا، وهو كيفية حذف العناصر من ملف ArrayList. سنناقش ذلك الآن. حذف عنصر من ArrayList - 1لقد ذكرنا بالفعل أن حذف العناصر من مصفوفة عادية ليس أمرًا مريحًا للغاية. نظرًا لأننا لا نستطيع حذف العنصر نفسه، فيمكننا فقط "إزالة" قيمته (ضبطها على قيمة خالية):
public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat[] cats = new Cat[3];
       cats[0] = new Cat("Thomas");
       cats[1] = new Cat("Behemoth");
       cats[2] = new Cat("Lionel Messi");

       cats[1] = null;

       System.out.println(Arrays.toString(cats));
   }


@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
الإخراج: [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] لكن تعيين عنصر مصفوفة على القيمة null يترك "فجوة". لم نقم بإزالة الموضع في المصفوفة، بل محتوياتها فقط. تخيل ماذا سيحدث إذا كان لدينا مجموعة مكونة من 50 قطة وقمنا بإزالة 17 منها بهذه الطريقة. سيكون لدينا مصفوفة بها 17 حفرة. فقط حاول تتبعهم! من غير الواقعي أن تتوقع أن تتذكر عدد الخلايا الفارغة التي يمكنك كتابة قيم جديدة فيها. إذا قمت بخطأ واحد، فسوف تقوم بالكتابة فوق مرجع الكائن الذي تريده. هناك، بالطبع، طريقة للقيام بذلك بعناية أكبر: بعد إزالة عنصر، انقل العناصر إلى مقدمة المصفوفة لوضع "الفتحة" في النهاية:
public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Thomas");
   cats[1] = new Cat("Behemoth");
   cats[2] = new Cat("Lionel Messi");
   cats[2] = new Cat("Fluffy");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       cats [i-1] = cats [i];// Move the elements to the front of the array, so the empty position is at the end
   }

   System.out.println(Arrays.toString(cats));
}
الإخراج: [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] يبدو هذا أفضل، لكن من الصعب وصفه بالحل القوي. إذا لم يكن هناك سبب آخر سوى حقيقة أنه يتعين علينا كتابة هذا الرمز في كل مرة نقوم فيها بحذف عنصر من مصفوفة! هذا خيار سيء. يمكننا أن نذهب بطريقة أخرى وننشئ طريقة منفصلة:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move the elements
}
ولكن هذا أيضًا قليل الفائدة: هذه الطريقة يمكن أن تعمل فقط مع Catالكائنات، وليس الأنواع الأخرى. بمعنى آخر، إذا كان لدى البرنامج 100 فئة أخرى نريد استخدامها مع المصفوفات، فسيتعين علينا كتابة نفس الطريقة بنفس المنطق تمامًا في كل منها. هذه كارثة كاملة -_- لكن ArrayListالفصل يحل هذه المشكلة! يطبق طريقة خاصة لإزالة العناصر:remove()
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
نقوم بتمرير فهرس كائننا إلى الطريقة التي تحذفه (تمامًا كما هو الحال في المصفوفة). تحتوي الطريقة remove()على ميزتين خاصتين. أولا، لا يترك "الثقوب". إنه يطبق بالفعل المنطق اللازم لتبديل العناصر عند إزالة عنصر من المنتصف، وهو ما كتبناه بأنفسنا سابقًا. انظر إلى الإخراج من الكود السابق:
[Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]

[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
قمنا بإزالة قطة واحدة من المنتصف، وتم نقل الباقي حتى لا تكون هناك مساحات فارغة. ثانيًا ، يمكنه حذف الكائنات ليس فقط عن طريق الفهرس (مثل المصفوفة العادية)، ولكن أيضًا عن طريق المرجع :
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);
   System.out.println(cats.toString());

   cats.remove(lionel);

   System.out.println(cats.toString());
}
الإخراج: [قطة {اسم='توماس'}، قطة{اسم='بيهيموث'}، قطة{اسم='ليونيل ميسي'}، قطة{اسم='رقيق'}] [قطة{اسم='توماس'}، Cat{name='Behemoth'}, Cat{name='Fluffy'}] يمكن أن يكون هذا مناسبًا جدًا إذا كنت لا تريد دائمًا تتبع فهرس الكائن المطلوب. يبدو أننا اكتشفنا الحذف العادي. الآن دعونا نتخيل هذا الموقف: نريد تكرار قائمتنا وإزالة قطة باسم محدد . للقيام بذلك، سنستخدم forحلقة سريعة (وتسمى أيضًا حلقة for-each)، والتي تعرفنا عليها في دروس ريشي:
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   for (Cat cat: cats) {

       if (cat.name.equals("Behemoth")) {
           cats.remove(cat);
       }
   }

   System.out.println(cats);
}
يبدو الرمز منطقيًا تمامًا. ولكن النتيجة قد تكون مفاجأة كبيرة: استثناء في مؤشر الترابط "الرئيسي" java.util.ConcurrentModificationException في java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) في java.util.ArrayList$Itr.next(ArrayList. java:831) في Cat.main(Cat.java:25) هناك نوع من الخطأ، وليس من الواضح سبب حدوثه. تتضمن هذه العملية عددًا من الفروق الدقيقة التي يجب معالجتها. إليك القاعدة العامة التي يجب أن تتذكرها: لا يمكنك التكرار على مجموعة وتغيير عناصرها في نفس الوقت. ونحن نعني أي نوع من التغيير، وليس مجرد الإزالة. إذا استبدلت عملية إزالة القطط بمحاولة إدخال قطط جديدة، فستكون النتيجة واحدة:
for (Cat cat: cats) {

   cats.add(new Cat("Salem Saberhagen"));
}

System.out.println(cats);
استثناء في مؤشر الترابط "الرئيسي" java.util.ConcurrentModificationException في java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) في java.util.ArrayList$Itr.next(ArrayList.java:831) في Cat.main( Cat.java:25) قمنا بتغيير عملية إلى أخرى، لكن النتيجة لم تتغير: لقد حصلنا على نفس ConcurrentModificationException . ويحدث ذلك على وجه التحديد عندما نحاول كسر القاعدة المذكورة أعلاه عن طريق تغيير القائمة أثناء التكرار عليها. في Java، نحتاج إلى كائن خاص يسمى المكرر (Iteratorالفئة) لحذف العناصر أثناء التكرار على مجموعة. الفصلIteratorمسؤول عن التكرار الآمن لقائمة العناصر. الأمر بسيط جدًا، لأنه يحتوي على 3 طرق فقط:
  • hasNext()- إرجاع صحيح أو خطأ، اعتمادًا على ما إذا كان هناك عنصر تالي في القائمة، أو أننا وصلنا بالفعل إلى العنصر الأخير.
  • next()- إرجاع العنصر التالي في القائمة
  • remove()- إزالة عنصر من القائمة
كما ترون، تم تصميم المكرِّر خصيصًا لتلبية احتياجاتنا، وفي الوقت نفسه لا يوجد شيء معقد بشأنه. لنفترض أننا نريد التحقق مما إذا كان هناك عنصر تالي في قائمتنا، وعرضه إذا كان هناك:
Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list

   Cat nextCat = catIterator.next();// Get the next element
   System.out.println(nextCat);// Display it
}
الإخراج: Cat{name='Thomas'} Cat{name='Behemoth'} Cat{name='Lionel Messi'} Cat{name='Fluffy'} كما ترون، فقد طبق ArrayListبالفعل طريقة خاصة لإنشاء مكرر : iterator(). بالإضافة إلى ذلك، لاحظ أنه عندما نقوم بإنشاء مكرر، فإننا نحدد فئة الكائنات التي سيعمل معها ( <Cat>). خلاصة القول هي أن المكرر يتعامل بسهولة مع مهمتنا الأصلية. على سبيل المثال، قم بإزالة القطة المسماة "ليونيل ميسي":
Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list

   Cat nextCat = catIterator.next();// Get the next element
   if (nextCat.name.equals("Lionel Messi")) {
       catIterator.remove();// Delete the cat with the specified name
   }
}

System.out.println(cats);
الإخراج: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] ربما لاحظت أننا لم نحدد الفهرس أو الاسم في remove()طريقة المكرر ! المكرِّر أكثر ذكاءً مما قد يبدو: فهو remove()يزيل العنصر الأخير الذي أرجعه المكرِّر. كما ترون، فقد فعل ما أردناه أن يفعله :) من حيث المبدأ، هذا هو كل ما تحتاج إلى معرفته حول إزالة العناصر من ملف ArrayList. حسنا، كل شيء تقريبا. في الدرس التالي، سننظر داخل هذا الفصل، ونرى ما يحدث هناك أثناء استدعاءات الأساليب المختلفة :) حتى ذلك الحين!
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION