JavaRush /وبلاگ جاوا /Random-FA /حذف یک عنصر از ArrayList در جاوا

حذف یک عنصر از 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("Hippopotamus");
       cats[2] = new Cat("Philip Markovich");

       cats[1] = null;

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


@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
نتیجه:

[Cat{name='Томас'}, null, Cat{name='Фorпп Маркович'}]
اما هنگام تنظیم مجدد، یک "سوراخ" در آرایه باقی می ماند. ما سلول را حذف نمی کنیم، بلکه فقط محتوای آن را حذف می کنیم. تصور کنید چه اتفاقی می افتد اگر یک آرایه از 50 گربه داشته باشیم که 17 تای آنها را به این ترتیب حذف کردیم. ما یک آرایه با 17 سوراخ خواهیم داشت و از آنها مراقبت می کنیم! به خاطر سپردن تعداد سلول های خالی که در آن می توانید مقادیر جدید بنویسید غیر واقعی است. یک بار اشتباه کنید و سلول را با ارجاع مورد نظر به شی بازنویسی می کنید. البته، فرصتی وجود دارد که این کار را با دقت بیشتری انجام دهید: پس از حذف، عناصر آرایه را به ابتدا منتقل کنید، به طوری که "سوراخ" در انتها باشد:
public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Thomas");
   cats[1] = new Cat("Hippopotamus");
   cats[2] = new Cat("Philip Markovich");
   cats[3] = new Cat("Fluff");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       //move the elements to the beginning so that the empty cell is at the end
       cats[i-1] = cats[i];
       cats[i] = null;
   }

   System.out.println(Arrays.toString(cats));
}
نتیجه:

[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}, null]
اکنون به نظر می رسد بهتر به نظر می رسد، اما به سختی می توان آن را یک راه حل پایدار نامید. حداقل به این دلیل که هر بار که عنصری را از آرایه حذف می کنیم باید این کد را با دست بنویسیم! گزینه بد می توانید از راه دیگری بروید و یک روش جداگانه ایجاد کنید:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...remove the cat by index and shift 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("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
ایندکس شی خود را به متد ارسال کردیم و حذف شد (درست مانند یک آرایه). روش remove()دارای دو ویژگی است. اولا ، "سوراخ" باقی نمی گذارد. در حال حاضر منطق تغییر عناصر را هنگام برداشتن یک عنصر از وسط، که قبلاً با دست نوشته بودیم، پیاده سازی می کند. به خروجی کد قبلی در کنسول نگاه کنید:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
یک گربه را از وسط بیرون آوردیم و بقیه را جابجا کردند تا شکافی وجود نداشته باشد. ثانیا ، می تواند یک شی را نه تنها با شاخص (مانند یک آرایه معمولی)، بلکه با ارجاع به شی حذف کند :
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(philipp);

   System.out.println(cats.toString());
}
نتیجه:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
اگر نمی خواهید همیشه شاخص شی مورد نظر را در ذهن خود نگه دارید، این می تواند بسیار راحت باشد. به نظر می رسد ما حذف معمول را مرتب کرده ایم. حال بیایید این وضعیت را تصور کنیم: می‌خواهیم فهرست عناصر خود را تکرار کنیم و گربه‌ای را با نام خاصی حذف کنیم. برای این کار از یک عملگر حلقه ویژه استفاده می کنیم for- for each. در این سخنرانی می توانید در مورد آن بیشتر بدانید .
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);

   for (Cat cat: cats) {

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

   System.out.println(cats);
}
به نظر می رسد کد کاملاً منطقی به نظر می رسد. با این حال، نتیجه ممکن است شما را شگفت زده کند:

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
نوعی خطا، و مشخص نیست که چرا ناگهان ظاهر شد. تعدادی از تفاوت های ظریف در این فرآیند وجود دارد که باید با آنها برخورد کرد. یک قانون کلی که باید به خاطر بسپارید: نمی‌توانید در یک مجموعه تکرار کنید و عناصر آن را همزمان تغییر دهید. بله، بله، دقیقا یک تغییر، و نه فقط یک حذف. اگر در کد ما سعی کنید حذف گربه ها را با گربه های جدید جایگزین کنید، نتیجه یکسان خواهد بود:
for (Cat cat: cats) {

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

System.out.println(cats);

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
ما یک عملیات را به دیگری تغییر دادیم، اما نتیجه تغییر نکرد: همان خطا ConcurrentModificationException. این دقیقا زمانی اتفاق می افتد که ما سعی می کنیم یک قانون را زیر پا بگذاریم و لیست را در حین تکرار آن تغییر دهیم. در جاوا، برای حذف عناصر در حین تکرار، باید از یک شی خاص - یک تکرار کننده (کلاس 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 next element
   System.out.println(nextCat);// print it to the console
}
نتیجه:

Cat{name='Томас'}
Cat{name='Бегемот'}
Cat{name='Фorпп Маркович'}
Cat{name='Пушок'}
همانطور که می بینید، کلاس 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 next element
   if (nextCat.name.equals("Philip Markovich")) {
       catIterator.remove();//delete the cat with the desired name
   }
}

System.out.println(cats);
نتیجه:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
ممکن است متوجه شده باشید که ما در متد iterator نه شاخص عنصر و نه نام متغیر مرجع را مشخص نکرده ایم remove()! تکرار کننده هوشمندتر از آن چیزی است که ممکن است به نظر برسد: این روش remove()آخرین عنصری را که توسط تکرار کننده برگردانده شده بود حذف می کند. همانطور که می بینید، دقیقاً همانطور که لازم بود کار کرد :) این اساساً همه چیزهایی است که باید در مورد حذف عناصر از ArrayList. دقیق تر - تقریباً همه چیز. در سخنرانی بعدی ما به "درون" این کلاس نگاه خواهیم کرد و خواهیم دید که در طول عملیات در آنجا چه اتفاقی می افتد :) می بینمت!
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION