Fjernelse af elementer fra Java Collections

Overblik

I denne hurtige vejledning vil vi tale om fire forskellige måder at fjerne elementer fra Java Collections på, som matcher visse forudsætninger.

Vi vil naturligvis også se på nogle af de forbehold, der er forbundet hermed.

Definering af vores Collection

Først vil vi illustrere to fremgangsmåder, der muterer den oprindelige datastruktur. Derefter vil vi tale om to andre muligheder, der i stedet for at fjerne elementerne opretter en kopi af den oprindelige Collection uden dem.

Lad os bruge følgende Collection gennem vores eksempler for at demonstrere, hvordan vi kan opnå det samme resultat ved hjælp af forskellige metoder:

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

Fjernelse af elementer med Iterator

Java’s Iterator giver os mulighed for både at gå og fjerne hvert enkelt element i en Collection.

For at gøre dette skal vi først hente en iterator over dens elementer ved hjælp af iterator-metoden. Herefter kan vi besøge hvert enkelt element ved hjælp af next og fjerne dem ved hjælp af remove:

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

Trods sin enkelhed er der nogle forbehold, som vi bør overveje:

  • Afhængigt af samlingen kan vi løbe ind i ConcurrentModificationException-undtagelser
  • Vi skal iterere over elementerne, før vi kan fjerne dem
  • Afhængigt af samlingen kan remove opføre sig anderledes end forventet. F.eks: ArrayList.Iterator fjerner elementet fra samlingen og forskyder efterfølgende data til venstre, hvorimod LinkedList.Iterator blot justerer pegeren til det næste element. Som sådan fungerer LinkedList.Iterator meget bedre end ArrayList.Iterator ved fjernelse af elementer

Java 8 og Collection.removeIf()

Java 8 introducerede en ny metode til Collection-grænsefladen, der giver en mere præcis måde at fjerne elementer på ved hjælp af Predicate:

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

Det er vigtigt at bemærke, at i modsætning til Iterator-tilgangen fungerer removeIf tilsvarende godt i både LinkedList og ArrayList.

I Java 8 tilsidesætter ArrayList standardimplementeringen – som er baseret på Iterator – og implementerer en anden strategi: Først iterer den over elementerne og markerer dem, der passer til vores Predicate; derefter iterer den en anden gang for at fjerne (og flytte) de elementer, der blev markeret i den første iteration.

Java 8 og indførelsen af Stream

En af de nye store funktioner i Java 8 var tilføjelsen af Stream (og Collectors). Der er mange måder at oprette en Stream fra en kilde på. De fleste operationer, der påvirker Stream-instansen, vil dog ikke mutere dens kilde, men API’et fokuserer snarere på at oprette kopier af en kilde og udføre enhver operation, vi måtte have brug for i dem.

Lad os se på, hvordan vi kan bruge Stream og Collectors til at finde/filtrere elementer, der matcher, og ikke matcher, vores Predicate.

5.1. Fjernelse af elementer med Stream

Fjernelse eller rettere filtrering af elementer ved hjælp af Stream er ret ligetil, vi skal blot oprette en instans af Stream ved hjælp af vores Collection, påkalde filter med vores Predicate og derefter indsamle resultatet ved hjælp af Collectors:

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

Streaming er mindre invasiv end de tidligere tilgange, det fremmer isolation og gør det muligt at oprette flere kopier fra den samme kilde. Vi skal dog huske på, at det også øger den hukommelse, der bruges af vores applikation.

5.2. Collectors.partitioningBy

Kombination af både Stream.filter og Collectors er ganske praktisk, selv om vi kan løbe ind i scenarier, hvor vi har brug for både matchende og ikke-matchende elementer. I sådanne tilfælde kan vi drage fordel af Collectors.partitioningBy:

Map<Boolean, List<String>> 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));

Denne metode returnerer et Map, der kun indeholder to nøgler, true og false, som hver peger på en liste, der indeholder henholdsvis de matchende og ikke-matchende elementer.

Konklusion

I denne artikel har vi set på nogle metoder til at fjerne elementer fra Collections og nogle af deres forbehold.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.