JavaRush /Java блогу /Random-KY /Javaдагы ArrayListтен элементти алып салуу

Javaдагы 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 тешиктүү массив болот жана аларга кам көрөлү! Жаңы маанилерди жаза турган бош уячалардын санын жатка эстеп калуу реалдуу эмес. Бир жолу ката кетирип, an objectке керектүү шилтеме менен уячаны кайра жазасыз. Албетте, муну бир аз кылдаттык менен жасоого мүмкүнчүлүк бар: жок кылгандан кийин, массивдин элементтерин башына жылдырыңыз, "тешик" аягында:
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]
Азыр ал жакшыраак көрүнөт, бирок муну туруктуу чечим деп атоого болбойт. Жок дегенде, анткени массивден элементти алып салган сайын бул codeду кол менен жазууга туура келет! Жаман вариант. Башка жол менен барып, өзүнчө ыкманы түзө аласыз:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...remove the cat by index and shift the elements
}
Бирок мунун да пайдасы жок: бул ыкма an objectтер менен гана иштей алат Cat, бирок башкалар менен иштей алbyte. Башкача айтканда, программада массивдерди колдонгубуз келген дагы 100 класс болсо, алардын ар бирине дал ошол логика менен бир эле ыкманы жазууга туура келет. Бул толук ийгorксиздик -_- Бирок ArrayList классында бул маселе ийгorктүү чечилди! Бул элементтерди алып салуу үчүн атайын ыкманы ишке ашырат - 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());
}
Биз an objectибиздин индексин методго өткөрдүк жана ал жок кылынды (массивдегидей). Метод remove()эки өзгөчөлүктөргө ээ. Биринчиден , ал "тешиктерди" калтырbyte. Ал мурунтан биз кол менен жазган элементти ортосунан алып салууда элементтерди алмаштыруу логикасын ишке ашырат . Консолдогу мурунку codeдун чыгышын караңыз:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
Бир мышыктын ортосунан чыгарып, калгандарын ары-бери жылдырдык, тешик калбасын деп. Экинчиден , ал an objectти индекси боюнча (кадимки массив сыяктуу) гана эмес, an objectке шилтеме аркылуу да жок кыла алат :
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='Пушок'}]
Эгер сиз каалаган an objectинин индексин дайыма башыңызда сактап калгыңыз келбесе, бул абдан ыңгайлуу болушу мүмкүн. Кадимки өчүрүүнү иреттеп алдык окшойт. Эми бул жагдайды элестетип көрөлү: биз элементтердин тизмесин кайталап, белгилүү бир ат менен мышыкты алып салгыбыз келет. Бул үчүн биз атайын цикл операторун колдонобуз 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)
Кандайдыр бир ката, эмне үчүн күтүлбөгөн жерден пайда болгону белгисиз. Бул процессте бир катар нюанстар бар, алар менен күрөшүү керек. Сиз эстен чыгарбоо керек болгон жалпы эреже: Сиз коллекцияны кайталап жана анын элементтерин бир эле учурда өзгөртө албайсыз. Ооба, ооба, так өзгөртүү, жана жөн эле жок кылуу эмес. Эгер сиз биздин codeубузда мышыктарды алып салууну жаңыларын салуу менен алмаштырууга аракет кылсаңыз, натыйжа бирдей болот:
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. Бул так эрежени бузуп, тизмени өзгөртүүгө аракет кылганда пайда болот. Java тorнде, итерация учурунда элементтерди алып салуу үчүн атайын an objectти - итераторду (класс ) колдонушуңуз керек Iterator. Класс Iteratorэлементтердин тизмесинен коопсуз өтүү үчүн жооптуу. Бул абдан жөнөкөй, анткени анын үч гана ыкмасы бар:
  • 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(). Ошондой эле, итераторду түзүүдө биз ал иштей турган an objectтердин классын аныктай турганыбызды эске алыңыз ( <Cat>). Акыр-аягы, биз итератордун жардамы менен баштапкы көйгөйүбүздү оңой эле чече алабыз. Мисалы, "Фorп Маркович" аттуу мышыкты жок кылалы:
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='Пушок'}]
Сиз итератор методунда элементтин индексин же шилтеме өзгөрмөнүн атын көрсөтпөгөнүбүздү байкаган чыгарсыз remove()! Итератор көрүнгөндөн акылдуураак: ыкма remove()итератор тарабынан кайтарылган акыркы элементти жок кылат. Көрүнүп тургандай, ал так керек болгон сыяктуу иштеди :) Бул, негизинен, элементтерди алып салуу жөнүндө бorшиңиз керек болгон нерселердин баары ArrayList. Тагыраак айтканда - дээрлик бардыгы. Кийинки лекцияда биз бул класстын "ичтерин" карап чыгабыз жана операциялар учурунда ал жерде эмне болуп жатканын көрөбүз :) Көрүшкөнчө!
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION