Vodič kroz TreeSet u Javi

1. Pregled

U ovom ćemo članku pogledati sastavni dio Java Collections Framework i jedan od najpopularnijih Postavi implementacije - TreeSet.

2. Uvod u TreeSet

Jednostavno rečeno, TreeSet je sortirana zbirka koja proširuje Skup sažetaka razred i provodi NavigableSet sučelje.

Evo kratkog sažetka najvažnijih aspekata ove implementacije:

  • Pohranjuje jedinstvene elemente
  • Ne čuva redoslijed umetanja elemenata
  • Razvrstava elemente u rastućem redoslijedu
  • Nije sigurno na niti

U ovoj implementaciji, objekti se sortiraju i pohranjuju uzlaznim redoslijedom prema njihovom prirodnom redoslijedu. The TreeSet koristi samobalansirajuće binarno stablo pretraživanja, točnije a Crveno-crno stablo.

Jednostavno rečeno, kao samobalansirajuće binarno stablo pretraživanja, svaki čvor binarnog stabla sastoji se od dodatnog bita koji se koristi za identificiranje boje čvora koji je crven ili crn. Tijekom sljedećih umetanja i brisanja, ti bitovi u boji pomažu u osiguravanju da drvo ostane više ili manje uravnoteženo.

Dakle, stvorimo instancu a TreeSet:

Postavi treeSet = novi TreeSet ();

2.1. TreeSet s paramerom za usporedbu konstruktora

Po želji možemo konstruirati a TreeSet s konstruktorom koji nam omogućuje definiranje redoslijeda sortiranja elemenata pomoću a Usporedive ili Usporednik:

Postavi treeSet = novi TreeSet (Comparator.comparing (String :: length));

Iako TreeSet nije nit-siguran, može se sinkronizirati izvana pomoću Collections.synchronizedSet () omot:

Postavi syncTreeSet = Collections.synchronizedSet (treeSet);

U redu, sada kad imamo jasnu ideju kako stvoriti TreeSet primjerice, pogledajmo uobičajene operacije koje imamo na raspolaganju.

3. TreeSetdodati()

The dodati() metoda, kako se očekuje, može se koristiti za dodavanje elemenata u TreeSet. Ako je element dodan, metoda se vraća pravi, inače - lažno.

Ugovor o metodi navodi da će se element dodati samo kada isti već nije prisutan u Postavi.

Dodajmo element u a TreeSet:

@Test public void whenAddingElement_shouldAddElement () {Set treeSet = new TreeSet (); assertTrue (treeSet.add ("Niz je dodan")); }

The dodati metoda je izuzetno važna jer detalji o provedbi metode ilustriraju kako TreeSet radi interno, kako koristi Karte stabalastaviti metoda za pohranu elemenata:

javna logička vrijednost add (E e) {return m.put (e, PRESENT) == null; }

Varijabla m odnosi se na unutarnju podlogu TreeMap (imajte na umu da TreeMap provodi NavigateableMap):

privatna privremena NavigableMap m;

Stoga je TreeSet interno ovisi o podlozi NavigableMap koji se inicijalizira instancom od TreeMap kada instanca TreeSet je stvoreno:

javni TreeSet () {this (novi TreeMap ()); }

Više o tome možete pronaći u ovom članku.

4. TreeSet sadrži ()

The sadrži () metoda koristi se za provjeru je li dati element prisutan u danom TreeSet. Ako je element pronađen, u suprotnom vraća true lažno.

Da vidimo sadrži () u akciji:

@Test public void whenCheckingForElement_shouldSearchForElement () {Set treeSetContains = new TreeSet (); treeSetContains.add ("Dodan je niz"); assertTrue (treeSetContains.contains ("Dodan je niz")); }

5. TreeSet remove ()

The ukloniti() metoda koristi se za uklanjanje navedenog elementa iz skupa ako je prisutan.

Ako je skup sadržavao navedeni element, ova metoda se vraća pravi.

Pogledajmo na djelu:

@Test public void whenRemovingElement_shouldRemoveElement () {Set removeFromTreeSet = new TreeSet (); removeFromTreeSet.add ("Dodan je niz"); assertTrue (removeFromTreeSet.remove ("Niz je dodan")); }

6. TreeSet clear ()

Ako želimo ukloniti sve predmete iz skupa, možemo koristiti čisto() metoda:

@Test public void whenClearingTreeSet_shouldClearTreeSet () {Set clearTreeSet = new TreeSet (); clearTreeSet.add ("Dodan je niz"); clearTreeSet.clear (); assertTrue (clearTreeSet.isEmpty ()); }

7. Veličina skupa stabala ()

The veličina() metoda koristi se za identificiranje broja elemenata prisutnih u TreeSet. To je jedna od osnovnih metoda u API-ju:

@Test public void whenCheckingTheSizeOfTreeSet_shouldReturnThesize () {Set treeSetSize = new TreeSet (); treeSetSize.add ("Dodan je niz"); assertEquals (1, treeSetSize.size ()); }

8. TreeSet isEmpty ()

The prazno je() metoda se može koristiti za utvrđivanje da li je zadana TreeSet instanca je prazna ili nije:

@Test public void whenCheckingForEmptyTreeSet_shouldCheckForEmpty () {Set emptyTreeSet = new TreeSet (); assertTrue (emptyTreeSet.isEmpty ()); }

9. TreeSet iterator ()

The iterator () metoda vraća iterator koji se ponavlja u rastućem redoslijedu nad elementima u Postavi. Ti su iteratori brzi.

Uzlazni redoslijed ponavljanja možemo promatrati ovdje:

@Test public void whenIteratingTreeSet_shouldIterateTreeSetInAscendingOrder () {Set treeSet = new TreeSet (); treeSet.add ("Prvo"); treeSet.add ("Drugo"); treeSet.add ("Treće"); Iterator itr = treeSet.iterator (); while (itr.hasNext ()) {System.out.println (itr.next ()); }}

Dodatno, TreeSet omogućuje nam ponavljanje kroz Postavi silaznim redoslijedom.

Da vidimo to na djelu:

@Test public void whenIteratingTreeSet_shouldIterateTreeSetInDescendingOrder () {TreeSet treeSet = new TreeSet (); treeSet.add ("Prvo"); treeSet.add ("Drugo"); treeSet.add ("Treće"); Iterator itr = treeSet.descendingIterator (); while (itr.hasNext ()) {System.out.println (itr.next ()); }}

The Iterator baca a ConcurrentModificationException if je skup modificiran u bilo kojem trenutku nakon stvaranja iteratora na bilo koji način, osim putem iteratora ukloniti() metoda.

Stvorimo test za ovo:

@Test (očekuje se = ConcurrentModificationException.class) javna praznina whenModifyingTreeSetWhileIterating_shouldThrowException () {Set treeSet = new TreeSet (); treeSet.add ("Prvo"); treeSet.add ("Drugo"); treeSet.add ("Treće"); Iterator itr = treeSet.iterator (); while (itr.hasNext ()) {itr.next (); treeSet.remove ("Second"); }} 

Alternativno, da smo koristili metodu uklanjanja iteratora, ne bismo naišli na iznimku:

@Test public void whenRemovingElementUsingIterator_shouldRemoveElement () {Set treeSet = new TreeSet (); treeSet.add ("Prvo"); treeSet.add ("Drugo"); treeSet.add ("Treće"); Iterator itr = treeSet.iterator (); while (itr.hasNext ()) {String element = itr.next (); if (element.equals ("Second")) itr.remove (); } assertEquals (2, treeSet.size ()); }

Ne postoji jamstvo brzog ponašanja iteratora jer je nemoguće dati bilo kakva tvrda jamstva u prisutnosti nesinkronizirane istodobne modifikacije.

Više o tome možete pronaći ovdje.

10. Prvo postavite stablo ()

Ova metoda vraća prvi element iz a TreeSet ako nije prazan. Inače, baca a NoSuchElementException.

Pogledajmo primjer:

@Test public void whenCheckingFirstElement_shouldReturnFirstElement () {TreeSet treeSet = new TreeSet (); treeSet.add ("Prvo"); assertEquals ("First", treeSet.first ()); }

11. TreeSet last ()

Analogno gornjem primjeru, ova metoda vratit će posljednji element ako skup nije prazan:

@Test public void whenCheckingLastElement_shouldReturnLastElement () {TreeSet treeSet = new TreeSet (); treeSet.add ("Prvo"); treeSet.add ("Posljednji"); assertEquals ("Posljednji", treeSet.last ()); }

12. TreeSet subSet ()

Ova metoda vratit će elemente u rasponu od odElementa do toElement. Imajte na umu da odElementa uključuje i toElement je ekskluzivno:

@Test public void whenUsingSubSet_shouldReturnSubSetElements () {SortedSet treeSet = new TreeSet (); treeSet.add (1); treeSet.add (2); treeSet.add (3); treeSet.add (4); treeSet.add (5); treeSet.add (6); Postavite očekivani set = novi TreeSet (); očekujeSet.add (2); očekujeSet.add (3); očekuje seSet.add (4); očekuje se Set.add (5); Postavi subSet = treeSet.subSet (2, 6); assertEquals (očekuje seSet, podskup); }

13. TreeSet headSet ()

Ova metoda vratit će elemente TreeSet koji su manji od navedenog elementa:

@Test public void whenUsingHeadSet_shouldReturnHeadSetElements () {SortedSet treeSet = new TreeSet (); treeSet.add (1); treeSet.add (2); treeSet.add (3); treeSet.add (4); treeSet.add (5); treeSet.add (6); Postavi subSet = treeSet.headSet (6); assertEquals (subSet, treeSet.subSet (1, 6)); }

14. TreeSet tailSet ()

Ova metoda vratit će elemente a TreeSet koji su veći ili jednaki navedenom elementu:

@Test public void whenUsingTailSet_shouldReturnTailSetElements () {NavigableSet treeSet = new TreeSet (); treeSet.add (1); treeSet.add (2); treeSet.add (3); treeSet.add (4); treeSet.add (5); treeSet.add (6); Postavi subSet = treeSet.tailSet (3); assertEquals (subSet, treeSet.subSet (3, true, 6, true)); }

15. Pohranjivanje Nula Elementi

Prije Jave 7 bilo je moguće dodati null elementi na prazan TreeSet.

Međutim, to se smatralo greškom. Stoga, TreeSet više ne podržava dodavanje null.

Kada dodamo elemente u TreeSet, elementi se sortiraju prema njihovom prirodnom redoslijedu ili prema nalogu komparator. Stoga dodavanje a null, u usporedbi s postojećim elementima, rezultira a NullPointerException od null ne može se usporediti ni s jednom vrijednošću:

@Test (očekuje se = NullPointerException.class) javna void kadaAddingNullToNonEmptyTreeSet_shouldThrowException () {Set treeSet = new TreeSet (); treeSet.add ("Prvo"); treeSet.add (null); }

Elementi umetnuti u TreeSet mora provesti Usporedive sučelje ili ga barem treba prihvatiti navedeni usporednik. Svi takvi elementi moraju se međusobno uspoređivati,tj.e1.compareTo (e2) ili usporednik.compored (e1, e2)ne smije baciti a ClassCastException.

Pogledajmo primjer:

class Element {privatni cijeli broj id; // Ostale metode ...} Usporednik usporedbe = (ele1, ele2) -> {return ele1.getId (). CompareTo (ele2.getId ()); }; @Test public void whenUsingComparator_shouldSortAndInsertElements () {Set treeSet = new TreeSet (comparator); Element ele1 = novi Element (); ele1.setId (100); Element ele2 = novi Element (); ele2.setId (200); treeSet.add (ele1); treeSet.add (ele2); System.out.println (treeSet); }

16. Izvedba TreeSet

U usporedbi s a HashSet izvedba a TreeSet je na donjoj strani. Operacije poput dodati, ukloniti i traži uzeti O (zapisnik n) vrijeme dok operacije poput ispisa n elementi poredani redoslijedom zahtijevaju Na) vrijeme.

A TreeSet trebao bi biti naš primarni izbor ako želimo da naši unosi budu sortirani kao TreeSet može im se pristupiti i prelaziti u uzlaznom ili silaznom redoslijedu, a izvedba uzlaznih operacija i pogleda vjerojatno će biti brža od one u silaznim redoslijedima.

Načelo lokalnosti - pojam je pojave u kojoj se često pristupa istim vrijednostima ili povezanim mjestima za pohranu, ovisno o obrascu pristupa memoriji.

Kad kažemo lokalitet:

  • Sličnim podacima često pristupa aplikacija sa sličnom učestalošću
  • Ako su dva unosa u blizini i imaju poredak, a TreeSet smješta ih jedno blizu drugog u strukturu podataka, a time i u memoriju

A TreeSet budući da smo struktura podataka s većim lokalitetom, stoga možemo zaključiti u skladu s Načelom lokaliteta, da bismo prednost trebali dati TreeSet ako nam nedostaje memorije i ako želimo pristupiti elementima koji su relativno blizu jedan drugome prema njihovom prirodnom rasporedu.

U slučaju da podatke treba čitati s tvrdog diska (koji ima veću latenciju od podataka očitanih iz predmemorije ili memorije), onda radije TreeSet jer ima veći lokalitet

17. Zaključak

U ovom smo članku usredotočeni na razumijevanje načina korištenja standarda TreeSet implementacija u Javi. Vidjeli smo njegovu svrhu i koliko je učinkovit u pogledu upotrebljivosti s obzirom na njegovu sposobnost izbjegavanja duplikata i razvrstavanja elemenata.

Kao i uvijek, isječke koda možete pronaći na GitHubu.