CodeGym /وبلاگ جاوا /Random-FA /حذف یک عنصر از ArrayList
John Squirrels
مرحله
San Francisco

حذف یک عنصر از ArrayList

در گروه منتشر شد
سلام! در درس آخر با کلاس آشنا شدیم ArrayListو نحوه انجام رایج ترین عملیات را با این کلاس یاد گرفتیم. ArrayListعلاوه بر این، ما به چندین تفاوت بین یک آرایه و یک آرایه معمولی اشاره کردیم . اما ما یک موضوع را دور زدیم، یعنی نحوه حذف عناصر از یک ArrayList. اکنون در مورد آن بحث خواهیم کرد. حذف یک عنصر از ArrayList - 1قبلاً اشاره کردیم که حذف عناصر از یک آرایه معمولی خیلی راحت نیست. از آنجایی که نمی‌توانیم خود عنصر را حذف کنیم، فقط می‌توانیم مقدار آن را «صفر» کنیم (تعدیل کنیم به null):

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='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}] [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] اگر نمی‌خواهید همیشه فهرست شی مورد نظر را دنبال کنید، این می‌تواند بسیار راحت باشد. به نظر می رسد ما حذف معمولی را کشف کرده ایم. حال بیایید این وضعیت را تصور کنیم: می‌خواهیم فهرست خود را تکرار کنیم و گربه‌ای را با نامی خاص حذف کنیم . forبرای انجام این کار، از یک حلقه سریع (که حلقه برای هر یک نیز نامیده می شود) استفاده می کنیم که در درس های Rishi با آن آشنا شدیم:

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);
استثنا در رشته "main" java.util.ConcurrentModificationException در java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) در java.util.ArrayList$Itr.next(ArrayList.java:831) در Cat. Cat.java:25) ما یک عملیات را به دیگری تغییر دادیم، اما نتیجه تغییر نکرد: همان ConcurrentModificationException را دریافت می کنیم . این دقیقاً زمانی اتفاق می‌افتد که سعی می‌کنیم با تغییر فهرست در حالی که روی آن تکرار می‌کنیم، قانون بالا را بشکنیم. در جاوا، ما به یک شی خاص به نام iterator (Iteratorکلاس) نیاز داریم تا موارد را در حین تکرار روی مجموعه حذف کنیم. کلاسIteratorمسئول تکرار ایمن بر روی لیست عناصر است. این بسیار ساده است، زیرا فقط 3 روش دارد:
  • hasNext()- بسته به اینکه مورد بعدی در لیست وجود داشته باشد یا قبلاً به آخرین مورد رسیده ایم، true یا false را برمی گرداند.
  • 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