Översikt
I den här korta handledningen kommer vi att prata om fyra olika sätt att ta bort element från Java Collections som matchar vissa predikat.
Vi kommer naturligtvis också att titta på några av de förbehåll som finns.
Definiera vår samling
Först kommer vi att illustrera två tillvägagångssätt som muterar den ursprungliga datastrukturen. Sedan ska vi prata om två andra alternativ som istället för att ta bort elementen skapar en kopia av den ursprungliga samlingen utan dem.
Låt oss använda följande samling genom våra exempel för att visa hur vi kan uppnå samma resultat med hjälp av olika metoder:
Collection<String> names = new ArrayList<>();names.add("John");names.add("Ana");names.add("Mary");names.add("Anthony");names.add("Mark");
Föra bort element med Iterator
Javas Iterator gör det möjligt för oss att både gå och ta bort varje enskilt element inom en samling.
För att göra detta måste vi först hämta en iterator över dess element med hjälp av iteratormetoden. Därefter kan vi besöka varje element med hjälp av next och ta bort dem med hjälp av remove:
Iterator<String> i = names.iterator();while(i.hasNext()) { String e = i.next(); if (e.startsWith("A")) { i.remove(); }}
Trots dess enkelhet finns det några varningar som vi bör tänka på:
- Avhängigt av samlingen kan vi stöta på ConcurrentModificationException- undantag
- Vi måste iterera över elementen innan vi kan ta bort dem
- Avhängigt av samlingen kan remove uppträda annorlunda än väntat. T.ex: ArrayList.Iterator tar bort elementet från samlingen och flyttar efterföljande data till vänster medan LinkedList.Iterator bara justerar pekaren till nästa element. Som sådan fungerar LinkedList.Iterator mycket bättre än ArrayList.Iterator när element tas bort
Java 8 och Collection.removeIf()
Java 8 introducerade en ny metod till Collection-gränssnittet som ger ett mer kortfattat sätt att ta bort element med hjälp av Predicate:
names.removeIf(e -> e.startsWith("A"));
Det är viktigt att notera att removeIf, i motsats till Iterator-metoden, fungerar på ett likartat sätt i både LinkedList och ArrayList.
I Java 8 åsidosätter ArrayList standardimplementationen – som förlitar sig på Iterator – och implementerar en annan strategi: först itererar den över elementen och markerar de som matchar vårt Predicate; därefter itererar den en andra gång för att ta bort (och förflytta) de element som markerades i den första iterationen.
Java 8 och införandet av Stream
En av de nya stora funktionerna i Java 8 var tillägget av Stream (och Collectors). Det finns många sätt att skapa en Stream från en källa. De flesta operationer som påverkar Stream-instansen kommer dock inte att mutera dess källa, utan API:et fokuserar snarare på att skapa kopior av en källa och utföra alla operationer som vi kan behöva i dem.
Låt oss ta en titt på hur vi kan använda Stream och Collectors för att hitta/filtrera element som matchar, och inte matchar, vårt Predicate.
5.1. Ta bort element med Stream
Att ta bort, eller snarare filtrera element med hjälp av Stream är ganska enkelt, vi behöver bara skapa en instans av Stream med hjälp av vår Collection, åberopa filter med vårt Predicate och sedan samla in resultatet med hjälp av Collectors:
Collection<String> filteredCollection = names .stream() .filter(e -> !e.startsWith("A")) .collect(Collectors.toList());
Streaming är mindre invasivt än de tidigare tillvägagångssätten, det främjar isolering och gör det möjligt att skapa flera kopior från samma källa. Vi bör dock komma ihåg att det också ökar minnet som används av vår applikation.
5.2. Collectors.partitioningBy
Att kombinera både Stream.filter och Collectors är ganska praktiskt, även om vi kan stöta på scenarier där vi behöver både matchande och icke-matchande element. I sådana fall kan vi dra nytta av 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));
Denna metod returnerar en Map som endast innehåller två nycklar, true och false, som var och en pekar på en lista som innehåller matchande respektive icke-matchande element.
Slutsats
I den här artikeln tittade vi på några metoder för att ta bort element från Collections och några av deras förbehåll.