A Hashtable már nem menő
Kedves kollégáim ihletésére született ez a poszt, nem az első, akiket jól belinkelek ide, amint hajlandóak végre blogot indítani.
Az blog poszt tartalma, hogy mi a különbség a Hashtable és a HashMap között.
Az eredeti ötlet onnan jött, hogy a Masterfield Oktatóközpont indított egy új blogot, melyen egy poszt arról szól, hogyan használjuk hatékonyan a Hashtable-t.
Ezen kicsit meglepődtem, hiszen a Java 1.2-es verziója óta, amikor is bevezették a collections framework-öt, én csak HashMap-et használok. De miért, milyen technológiai megfontolások állhatnak ennek a hátterében.
Mindkét osztály implementálja a Map interfészt, de nézzük meg a különbségeket:
- A legfőbb különbség, hogy amíg a Hashtable osztály szinkronizált, addig a HashMap osztály nem
- A HashMap engedélyezi null értékek használatát is
- A Hashtable nem követi a camelcase elnevezési konvenciót :)
- A Hashtable metódusai nincsenek ellátva a final módosítószóval, ahogy Bruce Eckel a Thinking in Java könyvében rámutat
Funkcionalitás szempontjából foglalkozzunk csak az első különbséggel. A collections framework azt mondja, hogyha a collections framework osztály példányait szinkronizálni akarjuk, akkor használjuk a Collections osztály metódusait, az adott példában a Collections.synchronizedMap statikus metódust.
Van-e különbség a Hashtable példányunk és a szinkronizált HashMap között? A válasz, hogy nincs, hiszen a két osztály működése nagyon hasonló, ez az osztálykönyvtár forráskódjából hamar kiderül. Sokan tesztelték, de az előző állításból is levezethető, hogy szignifikáns performancia különbség sincs a kettő között.
Akkor mi alapján válasszunk, döntsünk? Semmiképp ne a funkcionalitás alapján, mert ott nincs különbség. De mivel minden dokumentáció és könyv (a collections framework tutorial-ja, a Sun-os SCJP vizsgára felkészítő, és Bruce Eckel könyve is) a Hashtable-t legacy osztálynak minősíti, ezért kerüljük a használatát, hátha előbb-utóbb módosítás következik be kettőjük viszonyában. Persze feltehetőleg egyhamar nem fogják eltávolítani, depracated-dé tenni, hiszen annyi program használja. A különös, hogy erről a JavaDoc semmit nem ír.
Itt érdemes még megemlíteni, hogy a collections framework vezette be az Enumeration helyett az Iterator osztályt is. A Hashtable osztály elemein mindkét osztály példányával végigmehetünk, lásd a kódot. A values().iterator() ún. fail-fast iterator-t ad vissza, ami azt jelenti, hogy amennyiben nem az iterator módosító metódusain keresztül módosítjuk a Hashtable-t, kivétel fog keletkezni. Az Enumeration amúgy szintén egy ilyen maradványosztály a Java 1.0, 1.1 időkből, mely annyiban különbözik az Iterator-tól, hogy hosszabbak a metódusnevei, valamint nem rendelkezik egy remove() metódussal. Ezt viszont már megemlíti a JavaDoc, hogy az Iterator-t használjuk.
import java.util.ConcurrentModificationException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import junit.framework.Assert;
import org.junit.Test;
public class HashTest {
@Test
public void hashtableEnumeration() {
Hashtable hashtable = new Hashtable(){{
put("key0", "value");
}};
Enumeration enumeration = hashtable.elements();
hashtable.put("key1", "value1");
Assert.assertEquals("value1", enumeration.nextElement());
}
@Test(expected=ConcurrentModificationException.class)
public void hashtableIterator() {
Hashtable hashtable = new Hashtable(){{
put("key0", "value");
}};
Iterator iterator = hashtable.values().iterator();
hashtable.put("key1", "value1");
// ConcurrentModificationException kivetelt valt ki
iterator.next();
}
}