Премахване на елементи от Java колекции

1. Общ преглед

В този бърз урок ще говорим за четири различни начина за премахване на елементи от Java Collections, които съответстват на определени предикати.

Естествено ще разгледаме и някои от предупрежденията.

2. Определяне на нашата колекция

Първо ще илюстрираме два подхода, които мутират оригиналната структура на данните. Тогава ще говорим за две други опции, които вместо да премахнат елементите, ще създадат копие на оригиналната колекция без тях.

Нека използваме следната колекция в нашите примери, за да покажем как можем да постигнем един и същ резултат, използвайки различни методи:

Collection names = new ArrayList(); names.add("John"); names.add("Ana"); names.add("Mary"); names.add("Anthony"); names.add("Mark");

3. Премахване на елементи с Iterator

Java Iterator ни позволява едновременно да ходим и да премахваме всеки отделен елемент в колекция .

За целта първо трябва да извлечем итератор върху неговите елементи, използвайки метода на итератора . След това можем да посетим всеки елемент с помощта на next и да ги премахнем с помощта на remove :

Iterator i = names.iterator(); while(i.hasNext()) { String e = i.next(); if (e.startsWith("A")) { i.remove(); } }

Въпреки неговата простота, трябва да помислим за някои предупреждения:

  • В зависимост от колекцията може да срещнем изключения ConcurrentModificationException
  • Трябва да прегледаме елементите, преди да можем да ги премахнем
  • В зависимост от колекцията, премахването може да се държи по различен начин от очакваното. Например: ArrayList.Iterator премахва елемента от колекцията и измества следващите данни вляво, докато LinkedList.Iterator просто настройва показалеца към следващия елемент. Като такъв, LinkedList.Iterator се представя много по-добре от ArrayList.Iterator при премахване на елементи

4. Java 8 и Collection.removeIf ()

Java 8 въведе нов метод в интерфейса на Collection, който осигурява по-кратък начин за премахване на елементи с помощта на Predicate :

names.removeIf(e -> e.startsWith("A"));

Важно е да се отбележи, че противно на подхода на Iterator , removeIf се представя по същия начин както в LinkedList, така и в ArrayList .

В Java 8 ArrayList отменя внедряването по подразбиране - което разчита на Iterator - и прилага различна стратегия: първо, той прелиства елементите и маркира тези, които съответстват на предиката ни ; след това итерира втори път, за да премахне (и измести) елементите, маркирани в първата итерация.

5. Java 8 и въвеждането на поток

Една от новите основни функции в Java 8 беше добавянето на StreamCollectors ). Има много начини за създаване на поток от източник. Повечето операции, които засягат екземпляра на Stream, обаче няма да мутират източника му, а API се фокусира върху създаването на копия на източник и извършването на всяка операция, която може да ни е необходима в тях.

Нека да разгледаме как можем да използваме Stream и Collectors за намиране / филтриране на елементи, които съвпадат и не съвпадат с нашия предикат .

5.1. Премахване на елементи с поток

Премахването или по-скоро филтрирането на елементи с помощта на Stream е съвсем просто , просто трябва да създадем екземпляр на Stream, използвайки нашата колекция , да извикаме филтър с нашия предикат и след това да съберем резултата с помощта на Collectors:

Collection filteredCollection = names .stream() .filter(e -> !e.startsWith("A")) .collect(Collectors.toList());

Поточното предаване е по-малко инвазивно от предишните подходи, насърчава изолирането и позволява създаването на множество копия от един и същи източник. Трябва обаче да имаме предвид, че това също увеличава паметта, използвана от нашето приложение.

5.2. Колекционери.разделяне от

Комбинирането на Stream.filter и Collectors е доста удобно, въпреки че може да се сблъскаме със сценарии, при които имаме нужда както от съвпадащи, така и от несъвпадащи елементи. В такива случаи можем да се възползваме от Collectors.partitioningBy :

Map
    
      classifiedElements = names .stream() .collect(Collectors.partitioningBy((String e) -> !e.startsWith("A"))); String matching = String.join(",", classifiedElements.get(true)); String nonMatching = String.join(",", classifiedElements.get(false));
    

Този метод връща Карта, която съдържа само два ключа, true и false , всеки сочещ към списък, който съдържа съответстващите и несъответстващите елементи, съответно.

6. Заключение

В тази статия разгледахме някои методи за премахване на елементи от колекциите и някои от техните предупреждения.

Можете да намерите пълния изходен код и всички кодови фрагменти за тази статия в GitHub.