Yleiskatsaus
Tässä pikaoppaassa käsittelemme neljää eri tapaa poistaa elementtejä Java-kokoelmista, jotka täyttävät tietyt predikaatit.
Katsomme luonnollisesti myös joitakin varoituksia.
Kokoelmamme määrittely
Aluksi havainnollistamme kaksi lähestymistapaa, jotka muuttavat alkuperäistä tietorakennetta. Sitten puhumme kahdesta muusta vaihtoehdosta, jotka sen sijaan, että poistaisivat elementtejä, luovat kopion alkuperäisestä Collectionista ilman niitä.
Käytetään koko esimerkkimme ajan seuraavaa Collectionia havainnollistaaksemme, miten voimme saavuttaa saman tuloksen käyttämällä eri menetelmiä:
Collection<String> names = new ArrayList<>();names.add("John");names.add("Ana");names.add("Mary");names.add("Anthony");names.add("Mark");
Elementtien poistaminen Iteraattorilla
Javan Iteraattorin avulla pystymme sekä kävelemään että poistamaan jokaisen yksittäisen elementin Collectionin sisällä.
Tehdäksemme näin, meidän on ensin haettava iteraattori sen elementtien yli iteraattorimetodilla. Sen jälkeen voimme käydä jokaisen elementin läpi next-metodin avulla ja poistaa ne käyttämällä remove-metodia:
Iterator<String> i = names.iterator();while(i.hasNext()) { String e = i.next(); if (e.startsWith("A")) { i.remove(); }}
Yksinkertaisuudestaan huolimatta on joitakin varoituksia, jotka on syytä ottaa huomioon:
- Kokoelmasta riippuen saatamme törmätä ConcurrentModificationException-poikkeuksiin
- Meidän on käytävä läpi elementtien iteraattorilla, ennen kuin pystymme poistamaan elementit
- Kokoelmasta riippuen remove-metodi saattaa käyttäytyä odotetusta poiketen. Esim: ArrayList.Iterator poistaa elementin kokoelmasta ja siirtää seuraavat tiedot vasemmalle, kun taas LinkedList.Iterator vain säätää osoittimen seuraavaan elementtiin. Näin ollen LinkedList.Iterator toimii paljon paremmin kuin ArrayList.Iterator poistettaessa elementtejä
Java 8 ja Collection.removeIf()
Java 8 esitteli Collection-rajapintaan uuden metodin, joka tarjoaa tiiviimmän tavan poistaa elementtejä Predicaten avulla:
names.removeIf(e -> e.startsWith("A"));
On tärkeää huomata, että toisin kuin Iterator-lähestymistapa, removeIf toimii yhtä hyvin sekä LinkedListissä että ArrayListissä.
Javassa 8 ArrayList ohittaa oletustoteutuksen – joka luottaa Iteratoriin – ja toteuttaa erilaisen strategian: ensin se käy elementit läpi ja merkitsee ne elementit, jotka täsmäävät Predikaattiin; sen jälkeen se käy läpi toisen kerran poistaakseen (ja siirtääkseen) elementit, jotka merkittiin ensimmäisessä iteraatiossa.
Java 8 ja Streamin käyttöönotto
Yksi Java 8:n uusista tärkeistä ominaisuuksista oli Stream (ja Collectors). On monia tapoja luoda Stream lähteestä. Useimmat Stream-instanssiin vaikuttavat operaatiot eivät kuitenkaan muuta sen lähdettä, vaan API keskittyy pikemminkin luomaan kopioita lähteestä ja suorittamaan niissä tarvitsemamme operaatiot.
Katsotaanpa, miten voimme käyttää Streamia ja Collectoreita etsiessämme/suodatellessamme elementtejä, jotka sopivat tai eivät sovi Predikaattiin.
5.1. Stream ja Collectors. Elementtien poistaminen Streamin avulla
Elementtien poistaminen tai pikemminkin suodattaminen Streamin avulla on melko suoraviivaista, meidän tarvitsee vain luoda Instanssi Streamista käyttäen Collectioniamme, kutsua suodatin Predikaatillamme ja sitten kerätä tulos Collectorien avulla:
Collection<String> filteredCollection = names .stream() .filter(e -> !e.startsWith("A")) .collect(Collectors.toList());
Stream on vähemmän invasiivinen kuin edelliset lähestymistavat, se edistää eristämistä ja mahdollistaa useiden kopioiden luomisen samasta lähteestä. Meidän on kuitenkin pidettävä mielessä, että se myös lisää sovelluksemme käyttämää muistia.
5.2. Suoratoisto. Collectors.partitioningBy
Sekä Stream.filterin että Collectorsin yhdistäminen on varsin kätevää, vaikka saatamme törmätä skenaarioihin, joissa tarvitsemme sekä täsmääviä että yhteensopimattomia elementtejä. Tällaisissa tapauksissa voimme hyödyntää 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));
Tämä metodi palauttaa Mapin, joka sisältää vain kaksi avainta, true ja false, jotka kumpikin osoittavat listaan, joka sisältää yhteensopivat ja ei-yhteensopivat elementit.
Johtopäätös
Tässä artikkelissa tutustuimme joihinkin metodeihin, joiden avulla voimme poistaa elementtejä Collectionsista, sekä joihinkin niiden varoittaviin tekijöihin.