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: 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. 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.6. Zaključak