
Java programátor expert
Java HashTable (tabuľka) je dátová štruktúra, ktorá ti umožní bezpečne pracovať s dátami aj vo viacvláknových aplikáciách. Hoci ide o staršiu, no stále spoľahlivú implementáciu, jej používanie má svoje špecifiká. V nasledujúcich riadkoch sa pozrieme na to, ako trieda HashTable funguje, aké sú jej výhody a obmedzenia a tiež na to, kedy ju má zmysel použiť namiesto modernejších alternatív, ako je napríklad HashMap.
Trieda HashTable je jednou zo starších implementácií hashovacích tabuliek v Jave.
HashTable kolekcia – hierarchia dedičnosti (zdroj: media.geeksforgeeks.org/wp-content/uploads/20201124183400/HierarchyofHashtable.png)
HashTable je jednou z množstva dátových štruktúr na jednoduchšiu prácu s dátami. V našom seriáli si postupne predstavíme aj rôzne ďalšie dátové štruktúry. Ukážeme si, ako s nimi pracovať, aké operácie sa s nimi dajú robiť, aké sú najčastejšie používané metódy. Spomenieme výhody a nevýhody a kedy je vhodné konkrétnu dátovú štruktúru použiť.
Trieda HashTable je implementáciou rozhrania Map, ktorá ukladá páry kľúč-hodnota a využíva hashovaciu tabuľku pre rýchle operácie. Každý kľúč musí byť jedinečný a nikdy nesmie byť null, rovnako ani hodnota, teda v HashTable nie sú povolené null hodnoty.
Na rozdiel od modernejších kolekcií, ako je HashMap, je HashTable synchronizovaná, čo znamená, že je bezpečná pre použitie vo viacvláknových aplikáciách, ale zároveň jej operácie môžu byť pomalšie.
HashTable využíva hashCode() metódu objektov na rovnomerné rozdelenie párov do interných „vedier“. To umožňuje vykonávať operácie pridávania, vyhľadávania a odstraňovania v konštantnom čase (O(1)) pri ideálnom rozložení.
Pre vytvorenie inštancie HashTable máme k dispozícií niekoľko konštruktorov:
1. Prázdna HashTable
Vytvorí prázdnu hashovaciu tabuľku s predvolenou kapacitou (11) a load faktorom (0.75).
Hashtable<Integer, String> table = new Hashtable<>();
2.HashTable s definovanou kapacitou
Vytvorí novú, prázdnu hašovaciu tabuľku so zadanou počiatočnou kapacitou a predvoleným faktorom zaťaženia (0.75).
Hashtable<Integer, String> table = new Hashtable<>(20);
3. HashTable s definovanou kapacitou a load faktorom
Vytvorí novú, prázdnu hašovaciu tabuľku so zadanou počiatočnou kapacitou a zadaným faktorom zaťaženia.
Hashtable<Integer, String> table = new Hashtable<>(20, 0.82f);
4. HashTable skonštruovaný z Map
Vytvorí novú hašovaciu tabuľku s rovnakým mapovaním ako daná Map.
Hashtable(Map<? extends K,? extends V> t)
Inštancia Hashtable má dva parametre, ktoré ovplyvňujú jej výkon: počiatočnú kapacitu a faktor zaťaženia. Kapacita je počet segmentov v hašovacej tabuľke a počiatočná kapacita je jednoducho kapacita v čase vytvorenia hašovacej tabuľky. V prípade rovnakého hash hódu (tzv. kolízie) je v jednom segmente uložených viacero záznamov, ktoré je potrebné postupne prehľadávať. Koeficient zaťaženia udáva, ako veľmi sa môže hašovacia tabuľka naplniť, kým sa automaticky zvýši jej kapacita.
Vo všeobecnosti predvolený faktor zaťaženia (0.75) ponúka dobrý kompromis medzi nákladmi na čas a priestor. Vyššie hodnoty znižujú réžiu priestoru, ale zvyšujú časové náklady na vyhľadanie záznamu (čo sa odráža vo väčšine operácií hashtable vrátane get a put). Počiatočná kapacita riadi kompromis medzi premárneným priestorom a potrebou opakovaných operácií, ktoré sú časovo náročné.
Ak je počiatočná kapacita väčšia ako maximálny počet záznamov, ktoré bude hashovacia tabuľka obsahovať, vydelený jej koeficientom zaťaženia, nikdy sa nevyskytnú žiadne prehashovacie operácie. Nastavenie príliš vysokej počiatočnej kapacity však môže plytvať miestom. Pri konštruovaní HashTable preto odporúčame, čo najlepšie odhadnúť, koľko elementov budeme ukladať do kolekcie a zvoliť si správne počiatočnú kapacitu (a prípadne aj load factor).
Ak sa má do hashtable vytvoriť veľa záznamov, jeho vytvorenie s dostatočne veľkou kapacitou môže umožniť vkladanie záznamov efektívnejšie, ako nechať ho vykonávať automatické prehashovanie podľa potreby na zväčšenie kapacity tabuľky.
Dobre navrhnutá hashovacia funkcia pre objekty (hashCode) je nesmierne dôležitá, pretože práve ona rovnomerne rozdistribuuje elementy do správnych vedier (buckets), pričom znižuje pravdepodobnosť kolízií a udržiava efektívnosť a výkon kolekcie.
Pri vkladaní prvku do kolekcie sa vypočíta jeho hašovacia hodnota a určí sa segment, do ktorého by sa mal prvok uložiť. To samozrejme nestačí, pretože dva objekty s rovnakým hashCode nemusia byť rovnaké, preto v rámci toho istého vedra sa objekty porovnávajú pomocou metódy equals(), aby sa zabezpečila jedinečnosť vložených elementov.
Medzi základné operácie, ktoré môžeme vykonávať s HashTable, patria:
Kompletný prehľad metód triedy HashTable, nájdeš v oficiálnej dokumentácii.
HashTable je vhodná v nasledovných prípadoch:
Naopak, ak nepotrebujeme synchronizáciu alebo chceme podobnú kolekciu s vyšším výkonom, odporúča sa používať HashMap.
HashTable vyniká efektívnym ukladaním párov kľúč-hodnota a ich rýchlym vyhľadaním vďaka hashovacej metóde, keď ich potrebujeme načítať a použiť. Preto majú hashovacie tabuľky všestranné použitie.
Teraz si to predvedieme v programe, v ktorom najskôr HashTable vytvoríme pomocou konštruktora a uložíme do nej štáty susediace so Slovenskom. Následne si ešte ukážeme použitie niektorých základných metód pre prácu s HashTable.
Main.java
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Set;
public class Main {
public static void main(String[] args) {
// Hashtable creation
Hashtable<String, String> skNeighbors = new Hashtable<>();
// Adding country codes and country names
skNeighbors.put("SK", "Slovakia");
skNeighbors.put("CZ", "Czech Republic");
skNeighbors.put("AT", "Austria");
skNeighbors.put("HU", "Hungary");
skNeighbors.put("UA", "Ukraine");
skNeighbors.put("PL", "Poland");
// Display all country codes and names using Enumeration
System.out.println("Neighboring states of Slovakia:");
Enumeration<String> keys = skNeighbors.keys();
while (keys.hasMoreElements()) {
String code = keys.nextElement();
if(!code.equals("SK"))
System.out.println(code + " - " + skNeighbors.get(code));
}
// Lookup a country by its code
String searchCode = "DE";
if (skNeighbors.containsKey(searchCode)) {
System.out.println("\n✅ Country found for code " + searchCode + ": " + skNeighbors.get(searchCode));
} else {
System.out.println("\n❌ No country found for code " + searchCode);
}
// Display the total number of countries in the directory
System.out.println("\nTotal number of countries in the Hashtable (before remove): " + skNeighbors.size());
// Try to remove a country by its code which is not present
String removeCode = "DE";
String removedCountry = skNeighbors.remove(removeCode);
System.out.println("Removed country: " + removeCode + " - " + removedCountry);
// Try to remove a country by its code
removeCode = "SK";
removedCountry = skNeighbors.remove(removeCode);
System.out.println("Removed country: " + removeCode + " -> " + removedCountry);
// Check if a specific country exists in the table
String searchCountry = "Germany";
if (skNeighbors.containsValue(searchCountry)) {
System.out.println("✅ " + searchCountry + " is in the directory.");
} else {
System.out.println("❌ " + searchCountry + " is NOT in the directory.");
}
// Display the total number of countries in the directory
System.out.println("\nTotal number of countries in the Hashtable (after remove): " + skNeighbors.size());
// Clear all country entries
skNeighbors.clear();
System.out.println("Country directory cleared. Is it empty? " + skNeighbors.isEmpty());
// Display all remaining countries using keySet()
System.out.println("Remaining countries after clearing:");
Set<String> keySet = skNeighbors.keySet();
for (String code : keySet) {
System.out.println(code + " - " + skNeighbors.get(code));
}
}
}
Výstup z tohto príkladu je:
Pripravili sme pre teba súbory so spomínaným príkladom vo forme kódu, ktorý si môžeš spustiť priamo v Jave. Stiahni si Java kód pre HashTable tu.
Súvisiace články