JavaRush /Java blogi /Random-UZ /Java-da ArrayList-dan elementni olib tashlash

Java-da ArrayList-dan elementni olib tashlash

Guruhda nashr etilgan
Salom! Oxirgi ma'ruzada biz ArrayList sinfi bilan tanishdik , shuningdek, u bilan eng keng tarqalgan amallarni bajarishni o'rgandik. Bundan tashqari, biz ArrayList va oddiy massiv o'rtasidagi bir nechta farqlarni ta'kidladik. Endi ArrayList dan elementni olib tashlashni ko'rib chiqamiz. Biz allaqachon oddiy massivdagi elementlarni o'chirish juda qulay emasligini aytdik. ArrayList dan elementni olib tashlash - 1Hujayraning o'zini o'chira olmasligimiz uchun uning qiymatini faqat "nol" qilishimiz mumkin:
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 + '\'' +
               '}';
   }
}
Xulosa:

[Cat{name='Томас'}, null, Cat{name='Фorпп Маркович'}]
Ammo qayta o'rnatilganda, massivda "teshik" qoladi. Biz hujayrani o'chirmaymiz, faqat uning tarkibini o'chiramiz. Agar bizda 50 ta mushuk bo'lsa, nima bo'lishini tasavvur qiling, ulardan 17 tasini shu tarzda o'chirib tashladik. Bizda 17 ta teshikli massiv bo'ladi va ularga g'amxo'rlik qiling! Yangi qiymatlarni yozishingiz mumkin bo'lgan bo'sh hujayralar sonini yoddan eslab qolish haqiqiy emas. Bir marta xatoga yo'l qo'ying va siz ob'ektga kerakli havola bilan katakchani qayta yozasiz. Albatta, buni biroz ehtiyotkorlik bilan qilish imkoniyati mavjud: o'chirib tashlaganingizdan so'ng, "teshik" oxirida bo'lishi uchun massivning elementlarini boshiga o'tkazing:
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));
}
Xulosa:

[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}, null]
Endi u yaxshiroq ko'rinadi, ammo buni barqaror yechim deb atash qiyin. Hech bo'lmaganda, chunki har safar massivdan elementni olib tashlaganimizda ushbu kodni qo'lda yozishimiz kerak bo'ladi! Yomon variant. Siz boshqa yo'l bilan borishingiz va alohida usul yaratishingiz mumkin:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...remove the cat by index and shift the elements
}
Ammo bu ham kam foyda keltiradi: bu usul faqat ob'ektlar bilan ishlaydi Cat, lekin boshqalar bilan ishlay olmaydi. Ya'ni, agar biz massivlardan foydalanmoqchi bo'lgan dasturda yana 100 ta sinf mavjud bo'lsa, ularning har birida aynan bir xil mantiq bilan bir xil usulni yozishimiz kerak bo'ladi. Bu to'liq muvaffaqiyatsizlik -_- Lekin ArrayList sinfida bu muammo muvaffaqiyatli hal qilingan! U elementlarni olib tashlashning maxsus usulini qo'llaydi - 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());
}
Biz ob'ektimiz indeksini metodga o'tkazdik va u o'chirildi (xuddi massivdagi kabi). Usul remove()ikkita xususiyatga ega. Birinchidan , u "teshiklar" qoldirmaydi. U biz ilgari qo'lda yozgan elementni o'rtadan olib tashlashda elementlarni almashtirish mantiqini allaqachon amalga oshiradi . Konsolda oldingi kodning chiqishiga qarang:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
Biz bitta mushukni o'rtadan olib tashladik, qolganlari esa bo'shliqlar bo'lmasligi uchun harakatlantirildi. Ikkinchidan , u ob'ektni nafaqat indeks bo'yicha (odatiy massiv kabi), balki ob'ektga havola orqali ham o'chirishi mumkin :
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());
}
Xulosa:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Agar siz istalgan ob'ektning indeksini doimo boshingizda saqlashni istamasangiz, bu juda qulay bo'lishi mumkin. Biz odatdagi o'chirishni tartibga soldik. Keling, ushbu vaziyatni tasavvur qilaylik: biz elementlar ro'yxatini takrorlashni va ma'lum bir ismli mushukni olib tashlashni xohlaymiz. Buning uchun biz maxsus sikl operatoridan foydalanamiz for- for each. Ushbu ma'ruzada bu haqda ko'proq ma'lumot olishingiz mumkin .
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);
}
Kod juda mantiqiy ko'rinadi. Biroq, natija sizni hayratda qoldirishi mumkin:

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)
Qandaydir xato va nima uchun u to'satdan paydo bo'lganligi noma'lum. Ushbu jarayonda hal qilinishi kerak bo'lgan bir qator nuanslar mavjud. Eslab qolishingiz kerak bo'lgan umumiy qoida: siz bir vaqtning o'zida to'plamni takrorlay olmaysiz va uning elementlarini o'zgartira olmaysiz. Ha, ha, aynan o'zgarish va shunchaki o'chirish emas. Agar siz bizning kodimizda mushuklarni olib tashlashni yangilarini kiritish bilan almashtirishga harakat qilsangiz, natija bir xil bo'ladi:
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)
Biz bir operatsiyani boshqasiga o'zgartirdik, ammo natija o'zgarmadi: bir xil xato ConcurrentModificationException. Bu biz qoidani buzishga va uni takrorlash paytida ro'yxatni o'zgartirishga harakat qilganimizda sodir bo'ladi. Java-da, iteratsiya paytida elementlarni olib tashlash uchun siz maxsus ob'ektdan - iteratordan (sinf Iterator) foydalanishingiz kerak. Sinf Iteratorelementlar ro'yxati bo'ylab xavfsiz yurish uchun javobgardir. Bu juda oddiy, chunki u faqat 3 ta usulga ega:
  • hasNext()- ro'yxatda keyingi element bor-yo'qligiga yoki oxirgisiga yetib bo'lganimizga qarab trueqaytaradi .false
  • next()- ro'yxatning keyingi elementini qaytaradi
  • remove()- elementni ro'yxatdan o'chiradi
Ko'rib turganingizdek, iterator tom ma'noda bizning ehtiyojlarimizga "moslashtirilgan" va bu erda hech qanday murakkab narsa yo'q. Misol uchun, biz ro'yxatimizda quyidagi element mavjudligini tekshirmoqchimiz va agar shunday bo'lsa, uni konsolga chop eting:
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
}
Xulosa:

Cat{name='Томас'}
Cat{name='Бегемот'}
Cat{name='Фorпп Маркович'}
Cat{name='Пушок'}
Ko'rib turganingizdek, sinf ArrayListallaqachon iterator yaratish uchun maxsus usulni amalga oshiradi - iterator(). Shuni ham yodda tutingki, iterator yaratishda biz u ishlaydigan ob'ektlar sinfini belgilaymiz ( <Cat>). Oxir oqibat, biz iterator yordamida asl muammomizni osongina hal qilishimiz mumkin. Masalan, "Filip Markovich" ismli mushukni o'chirib tashlaylik:
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);
Xulosa:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Biz iterator usulida element indeksini ham, mos yozuvlar o'zgaruvchisi nomini ham ko'rsatmaganimizni payqagandirsiz remove()! Iterator tuyulishi mumkin bo'lgandan ko'ra aqlliroq: usul remove()iterator tomonidan qaytarilgan oxirgi elementni olib tashlaydi. Ko'rib turganingizdek, u kerakli darajada ishladi :) dan elementlarni olib tashlash haqida bilishingiz kerak bo'lgan hamma narsa ArrayList. Aniqrog'i - deyarli hamma narsa. Keyingi ma'ruzada biz ushbu sinfning "ichki qismi" ni ko'rib chiqamiz va operatsiyalar paytida u erda nima sodir bo'lishini ko'ramiz :) Ko'rishguncha!
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION