Vodič za HashSet na Javi

1. Pregled

U ovom ćemo članku zaroniti u HashSet. Jedan je od najpopularnijih Postavi implementacije, kao i sastavni dio Okvira Java Collections.

2. Uvod u HashSet

HashSet jedna je od temeljnih struktura podataka u API-ju Java Collections.

Prisjetimo se najvažnijih aspekata ove provedbe:

  • Pohranjuje jedinstvene elemente i dopušta nule
  • Potkrijepljeno je s HashMap
  • Ne održava redoslijed umetanja
  • Nije sigurno na niti

Imajte na umu da je ovo interno HashMap dobiva inicijalizaciju kada instanca HashSet je stvoreno:

javni HashSet () {map = novi HashMap (); }

Ako želite ući dublje u to kako HashMap djela, članak o njemu usredotočen možete pročitati ovdje.

3. API

U ovom ćemo dijelu pregledati najčešće korištene metode i pogledati nekoliko jednostavnih primjera.

3.1. dodati()

The dodati() metoda može se koristiti za dodavanje elemenata u skup. Ugovor o metodi navodi da će se element dodati samo kada već nije prisutan u skupu. Ako je element dodan, metoda se vraća pravi, inače - lažno.

U a možemo dodati element HashSet Kao:

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

Iz perspektive provedbe, dodati metoda je izuzetno važna. Detalji implementacije ilustriraju kako HashSet radi interno i koristi HashMap-astaviti metoda:

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

The karta varijabla je referenca na internu, podlogu HashMap:

privatna privremena karta HashMap-a;

Bilo bi dobro upoznati se s hashcode prvo da se dobije detaljno razumijevanje kako su elementi organizirani u strukturama podataka temeljenim na raspršivanju.

Rezimirajući:

  • A HashMap je niz od kante sa zadanim kapacitetom od 16 elemenata - svaki segment odgovara različitoj hashcode vrijednosti
  • Ako različiti objekti imaju istu vrijednost hashcode-a, pohranjuju se u jedan segment
  • Ako je faktor opterećenja dosegne se novi niz koji se kreira dvostruko veći od prethodnog, a svi se elementi preoblikuju i redistribuiraju među nove odgovarajuće segmente
  • Da bismo dohvatili vrijednost, hashiramo ključ, modiramo ga, a zatim idemo u odgovarajući segment i pretražujemo potencijalno povezani popis u slučaju da postoji više od jednog objekta

3.2. sadrži ()

Svrha sadrži metoda je provjeravati je li element prisutan u danom HashSet. Vraća se pravi ako je element pronađen, u suprotnom lažno.

Možemo provjeriti postoji li element u HashSet:

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

Kad god se objekt preda ovoj metodi, izračunava se vrijednost hasha. Zatim se odgovarajuće mjesto segmenta rješava i prelazi.

3.3. ukloniti()

Metoda uklanja navedeni element iz skupa ako je prisutan. Ova metoda se vraća pravi ako je skup sadržavao navedeni element.

Pogledajmo radni primjer:

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

3.4. čisto()

Ovu metodu koristimo kada namjeravamo ukloniti sve stavke iz skupa. Temeljna implementacija jednostavno briše sve elemente iz temeljne HashMap.

Da vidimo to na djelu:

@Test public void whenClearingHashSet_shouldClearHashSet () {Set clearHashSet = new HashSet (); clearHashSet.add ("Dodan niz"); clearHashSet.clear (); assertTrue (clearHashSet.isEmpty ()); }

3.5. veličina()

Ovo je jedna od temeljnih metoda u API-ju. Intenzivno se koristi jer pomaže u prepoznavanju broja elemenata prisutnih u HashSet. Temeljna implementacija jednostavno delegira izračun na Veličina HashMap-a () metoda.

Da vidimo to na djelu:

@Test public void whenCheckingTheSizeOfHashSet_shouldReturnThesize () {Set hashSetSize = new HashSet (); hashSetSize.add ("Dodan niz"); assertEquals (1, hashSetSize.size ()); }

3.6. prazno je()

Ovu metodu možemo koristiti za utvrđivanje je li zadana instanca a HashSet je prazan ili nije. Ova metoda se vraća pravi ako skup ne sadrži elemente:

@Test public void whenCheckingForEmptyHashSet_shouldCheckForEmpty () {Set emptyHashSet = new HashSet (); assertTrue (emptyHashSet.isEmpty ()); }

3.7. iterator ()

Metoda vraća iterator preko elemenata u Postavi. Elementi se posjećuju bez određenog redoslijeda, a iteratori su brzi.

Ovdje možemo promatrati redoslijed slučajnih ponavljanja:

@Test public void whenIteratingHashSet_shouldIterateHashSet () {Set hashset = new HashSet (); hashset.add ("Prvo"); hashset.add ("Drugi"); hashset.add ("Treće"); Iterator itr = hashset.iterator (); while (itr.hasNext ()) {System.out.println (itr.next ()); }}

Ako je skup modificiran u bilo koje vrijeme nakon stvaranja iteratora na bilo koji način, osim vlastitom metodom uklanjanja iteratora, Iterator baca a ConcurrentModificationException.

Da vidimo to na djelu:

@Test (očekuje se = ConcurrentModificationException.class) javna praznina whenModifyingHashSetWhileIterating_shouldThrowException () {Set hashset = new HashSet (); hashset.add ("Prvo"); hashset.add ("Drugi"); hashset.add ("Treće"); Iterator itr = hashset.iterator (); while (itr.hasNext ()) {itr.next (); hashset.remove ("Drugi"); }} 

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

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

Ponašanje iteratora u slučaju neuspjeha ne može se zajamčiti jer je nemoguće dati bilo kakva tvrda jamstva u prisutnosti istodobno sinkronizirane izmjene.

Neuspješni iteratori bacaju ConcurrentModificationException na najbolji način. Stoga bi bilo pogrešno napisati program koji je svojom ispravnošću ovisio o ovoj iznimci.

4. Kako HashSet Održava jedinstvenost?

Kada stavimo predmet u HashSet, koristi objekt hashcode vrijednost za utvrđivanje je li element već u skupu.

Svaka vrijednost hash koda odgovara određenom mjestu segmenta koje može sadržavati različite elemente, za koje je izračunata hash vrijednost jednaka. Ali dva predmeta s istim hashCode možda neće biti jednako.

Dakle, objekti u istom segmentu uspoređivat će se pomoću jednako () metoda.

5. Izvedba programa HashSet

Izvedba a HashSet utječu uglavnom dva parametra - njegov Početni kapacitet i Faktor opterećenja.

Očekivana vremenska složenost dodavanja elementa skupu je O (1) koja može pasti na Na) u najgorem slučaju (prisutna samo jedna kanta) - dakle, bitno je zadržati pravo HashSet-ovi kapacitet.

Važna napomena: od JDK 8, najgora je vremenska složenost O (zapisnik * n).

Faktor opterećenja opisuje koja je maksimalna razina punjenja, iznad koje će trebati smanjiti skup.

Također možemo stvoriti i HashSet s prilagođenim vrijednostima za početni kapacitet i faktor opterećenja:

Postavi hashset = novi HashSet (); Postavi hashset = novi HashSet (20); Postavi hashset = novi HashSet (20, 0,5f); 

U prvom se slučaju koriste zadane vrijednosti - početni kapacitet 16 i faktor opterećenja 0,75. U drugoj nadjačavamo zadani kapacitet, au trećoj nadjačavamo obje.

Nizak početni kapacitet smanjuje složenost prostora, ali povećava učestalost ponovnog usišljavanja, što je skup postupak.

S druge strane, veliki početni kapacitet povećava troškove iteracije i početnu potrošnju memorije.

U pravilu:

  • Visok početni kapacitet dobar je za velik broj unosa zajedno s malo ili nimalo ponavljanja
  • Nizak početni kapacitet dobar je za nekoliko unosa s puno ponavljanja

Stoga je vrlo važno postići točnu ravnotežu između njih dvoje. Obično je zadana implementacija optimizirana i funkcionira sasvim u redu, ako osjetimo potrebu prilagoditi ove parametre u skladu sa zahtjevima, moramo to učiniti razumno.

6. Zaključak

U ovom smo članku iznijeli korisnost a HashSet, njegova svrha kao i osnovni rad. Vidjeli smo koliko je učinkovit u pogledu upotrebljivosti s obzirom na njegove stalne vremenske performanse i sposobnost izbjegavanja duplikata.

Proučavali smo neke od važnih metoda iz API-ja, kako nam mogu pomoći kao programeru da koristimo HashSet svom potencijalu.

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