2 Aralık 2011 Cuma

Hızlı Arama Zamanlı Kaplar-1

Dizi🔎, Vector🔎 veya liste yapısını gerçekleştiren ArrayList🔎 ve LinkedList🔎 gibi sınıfların bir zaafı, doğrusal bir yapıya sahip olmaları nedeniyle ortalama eleman arama zamanlarının yavaş olmasıdır. Örnek olarak, sırasız bir listede gözlerinizi baştan sona doğru gezdirerek adınızı aradığınızı düşünün. Yapmanız gereken, listenin başından adınızın bulunduğu yere kadar her sıradaki ad ile kendi adınızın eşitlik denetimini yapmaktır. Şanslıysanız, adınız başlardadır ve kısa zamanda kendinize dair bilgileri bulabilirsiniz; ancak, şansınızın yardım etmediği bir gün adınız listenin sonunda da olabilir ve aradığınız bilgiyi çok daha uzun bir zamanda elde edersiniz. Aynı listede adını arayanların harcadığı ortalama zaman ise—n kişi için toplam 1 + 2 + ... + n eşitlik denetimi olduğuna göre—n(n+1)/2n, yani (n+1)/2, eşitlik denetimi zamanı olacaktır.

Her eşitlik denetimi sonrasında arama uzayını sadece bir küçülten bu yöntem işi görür fakat büyük listelerde işkence halini alan bir performansa sahiptir. Biraz düzenli bir yapıya sahip olanlarınız, performans çözümlemede O(n) şeklinde ifade edilen bu sonucun aşağılara çekilmesinin olanaklı olduğunu bilirler. Mesela, bir kütüphaneye yolu düşüp de kitap arayanlarınız—iyi örnek olmadı galiba... müzik dükkânından albüm alanlarınız diyelim—hemen konuya [ya da tarza] göre indeksleme ve aynı konu içinde alfabetik sıraya dizmeyi önereceklerdir. Java standart kitaplığında doğrudan karşılığı olmadığı için, indekslemeyi köşeye koyup sıralamanın bize ne kazandırabileceğini görelim; bunu yaparken de—unutmayın, asıl konumuz işi bilgisayarlara yaptırmak—adımızın özelliklerine dayalı eniyilemelerin yapılamadığını varsayalım. İyi bir arama zamanını garanti etmek için izlenmesi gereken strateji, her eşitlik denetimi sonrasında arama uzayını olabildiğince yüksek oranda küçültmelidir. Konulan bu hedefe, "İlk elemanın ad bilgisi adıma eşit mi?" şeklinde sorulan sorunun "Ortadaki elemanın ad bilgisi adıma eşit mi, eşit değilse adımdan önce mi geliyor, sonra mı?" şeklinde değiştirilmesiyle erişilebilir. Böylesine bir değişiklik arama uzayımızı bir azaltmak yerine ikiye bölerek küçültecektir. Çünkü, sıralı olduğu bilinen listemizin orta elemanının aradığımız değere eşit olmaması durumunda, adımız ya ilk yarıdadır ya da ikinci yarıda; böylece, adımız hangi yarıda olursa olsun, arama uzayımız yarı yarıya küçülecektir. Bu ise, sorgumuzun olumlu veya olumsuz sonucunun O(n) yerine O(lgn) performansla verileceği anlamını taşır. İkili arama adı verilen ve java.util paketindeki Arrays🔎 ve Collections sınıflarının binarySearch metotlarında gerçekleştirilen bu algoritmanın ifade edilen performansı verebilmesi için veri kümesinin sıralı ve altyapıda kullanılan veri yapısının doğrudan erişimli dizi, Vector veya ArrayList gibi bir yapı olması gerekir. Çok özel durumlar1 dışında sıralamanın O(nlgn) ve daha fazla bir ek yük getireceği düşünüldüğünde, bu algoritmanın, özellikle de ekleme ve silmelerle veri kümesinin büyüyüp küçüldüğü kullanım desenlerinde, her zaman sözü edilen performansı sağlayamayacağı görülür. Çözüm, ekleme/silme işlemlerinin de hızlı bir şekilde yapıldığı hızlı arama zamanlı kaplar olan ve iki yazılık dizimizin konusunu oluşturan eşlemlerdedir.


Giriş


Standart Java kitaplığına bakıldığında eşlemlerin Veri Kapları Çerçevesi'nin bir parçası olarak java.util paketi içinde tanımlanıp gerçekleştirildiği görülür. Temel işlevselliği tanımlayan Map arayüzü ve kimi işlemlerin altyapıda kullanılan veri yapısından bağımsız bir şekilde gerçekleştirildiği AbstractMap sınıfının temelini oluşturduğu eşlemlere dair sıradüzeninin önemli bir bölümü aşağıda verilmiştir. Object, Cloneable ve Serializable dışındakilerin java.util paketinde olduğu türlerden Hashtable, Java'nın ilk uyarlamasından itibaren var olup J2SDK 1.2 ile gelen Veri Kapları Çerçevesi'ne monte edilmiştir. Dolayısıyla, J2SDK 1.2 öncesi yazılmış programları çökertmemek adına Hashtable sınıfı, AbstractMap'ten kalıtlamaktansa, Map arayüzünü doğrudan gerçekleştirmiştir. Kadük olan Dictionary adlı soyut sınıfın kullanımından ise kesinlikle kaçınılmalıdır.
Map arayüzü ile başlamadan önce, nasıl kullanılabileceğini örneklendirerek eşlemlerin biçimsel olmayan tanımını yapalım. Eşlemler, anahtar-tutanak çifti olarak temsil edilen eşlemelerin hızlı bir biçimde işlenmesini sağlayacak şekilde düzenlenerek tutulduğu veri yapılarıdır. Doğal olarak temel işlemler, eşleme bir anahtar-tutanak çiftinin eklenmesi, sağlanan bir anahtara dair tutanak bilgisinin sorgulanması veya eşlemenin silinmesidir. Bu işlemlerin hızlı bir şekilde tamamlanması ise gerçekleştirimci tarafından karşılanacağı varsayılan temel bir beklentidir.

Kimi eşlemelerin eşlemlerin yanısıra diğer veri yapıları ile de temsil edilebileceği bir gerçektir. Örnek olarak, ülkemizdeki plaka kodları ile il adları arasındaki eşlemeleri düşünün. Söz konusu eşlemeler bir eşlemle tutulabileceği gibi, doğrudan erişimi destekleyen bir veri yapısı kullanılarak da tutulabilir. Aslına bakarsanız, veri kümemizin durağan yapısı—her dakika yeni bir il eklenmeyecek—ve plaka kodları ile doğrudan erişimde kullanılan indis arasındaki neredeyse mükemmel uyuşma nedeniyle—0 nolu indis boş bırakılırsa 1-1 bir uyuşmadan söz edebiliriz—dizinin kullanılması çok daha yerinde bir seçim olacaktır. Ancak; eşlemenin ters yönde (İl adı→Plaka kodu) olmasının istenmesi halinde işler değişir: doğrudan erişimden yararlanılabilmesi il adının kıyımdan geçirilerek bir tamsayıya dönüştürülmesi ile olanaklıdır. Bu gibi bir durumda, fazladan kıyım fonksiyonu yazıp belki de düşük verimli bir çözüm üretmektense, eşlem kullanmak daha doğru olacaktır.

Eşlemleri çekici kılan bir diğer kullanım deseni, veri kümesinin anahtar-tutanak eşlemelerinin eklenmesi ve silinmesi ile büyüyüp küçüldüğü problemlerdir. Bu gibi durumlarda, kıyım fonksiyonunun maliyetine ek olarak, altyapıda kullanılan veri yapısının büyümesi veya küçülmesi ve ekleme ile silmelerin kaydırmalar nedeniyle sebep olacağı maliyet, eşlem kullanımını cazip hale getirmektedir.

Map Arayüzü ve AbstractMap Sınıfı


Gelelim, Map arayüzüne. Anahtar ve tutanak bilgisi türlerinin tür parametresi olarak tanımlandığı bu soysal arayüzdeki isEmpty iletisi, hedef eşlemin boş olup olmadığı sorusuna yanıt verirken, size iletisi hedef eşlemdeki eşleme sayısını döndürür. Bu bağlamda, standart kitaplıkta sağlanan gerçekleştirimlerin aynı anahtara karşılık tek bir tutanak bilgisi tuttuğu hatırlatılmalıdır.2

Eşlemlere ekleme, argüman olarak, sırasıyla, anahtar ve tutacak bilgisini alan put iletisi ile yapılabilir. Anahtar değerinin eşlendiği bir önceki tutanağı döndüren bu işlemin aynı anahtar değeri kullanılarak birden çok kez icra edilmesi durumunda, en son ekleme etkili olacaktır. Birden çok sayıda eşlemenin eklenmesinin istenmesi halinde, arda arda kullanılacak put iletileri yerine eklenecek anahtar-tutanak bilgilerini içeren bir eşlem bekleyen putAll iletisi kullanılabilir.

get iletisi argümanındaki anahtarın hedef eşlemde eşlendiği tutanak bilgisini döndürür. Benzer bir ileti, kendisine geçirilen anahtarın bir tutanak ile eşlenip eşlenmediğini döndüren containsKey yüklemidir. containsValue ise containsKey yükleminin tersi olarak düşünülebilir; bu yüklem, argümanındaki tutanak bilgisinin herhangi bir anahtar ile eşlenip eşlenmediği sorusunu yanıtlar. Bir an için, get iletisinin containsKey iletisini gereksiz kıldığı düşünülebilir. Ne var ki, get iletisinin eşlemde olmayan bir anahtarın sorgulanmasına yanıt olarak null döndürmesi ve kimi eşlem yapısı gerçekleştirimlerinde anahtarların null ile eşlenebilir olması sonucu ortaya çıkan muğlaklık ancak containsKey iletisinin kullanımıyla ortadan kaldırılabilir. containsKey aynı anahtar için true döndürüyorsa anahtar null değerine eşlenmiş demektir, aksi takdirde anahtara dair bir eşleme yoktur.

Hedef eşlemden eleman silmek için iki ileti kullanılabilir. Bunlardan clear, eşlemi hızlı bir biçimde boşaltırken, remove argümanındaki anahtara ilişkin eşlemeyi silmekle yetinir ve sonucu olarak işlem öncesinde anahtarın ilişkilendirildiği tutanak bilgisini döndürür. Geçirilen anahtar değerinin hedef eşlemde bulunmaması halinde ise, remove bu gerçeği kullanıcısına null döndürerek bildirir.

Map arayüzünü gerçekleştiren sınıfların desteklemesi gereken bir diğer ileti grubu, hedef eşlemdeki anahtar-tutanak eşlemelerinin değişik görüntülerini sağlayanlardır. Bu gruptaki iletiler tarafından döndürülen kaplar ile hedef eşlemin belleği paylaştıkları ve dolayısıyla görüntü üzerinden yapılan değişikliklerin eşleme de yansıtılacağı akılda tutulmalıdır. Bunlardan keySet, anahtarları barındıran küme nesnesini gösteren bir java.util.Set tutacağı döndürürken, values iletisi tutanak bilgilerini içeren nesneyi temsil eden bir java.util.Collection tutacağı döndürür. İlk ileti için Set ikinci ileti içinse Collection türlü bir tutacak döndürülmesinin nedeni, eşlemlerin aynı anahtardan en fazla bir tane bulundururken, aynı tutanak bilgisinin birden çok sayıda eşlemede geçmesinin mümkün olmasıdır. Örnek olarak; anahtar türü String, tutanak türü Integer tanımlanmış bir eşleme "İzmir"→35 ve "Karşıyaka"→35 bilgilerinin eklendiğini varsayalım. Bu eşleme gönderilecek values iletisi, Collection tutacağı ile gösterilen iki elemanlı bir kap döndürürecektir: halbuki, dönüş türü olarak Set'in kullanılması her iki değerin de 35 olması nedeniyle tek elemanlı bir küme döndürülmesine neden olacaktı.

values ve keySet iletileri gibi görüntüleme grubuna ait olan entrySet, hedef eşlemdeki anahtar-tutanak çiftlerini bir küme içinde döndürür. Kümelerin tek tür ile parametrize edilebiliyor olmasından ötürü, döndürülen küme anahtar ve tutacak türleri ile parametrize edilen Map.Entry ile gösterilen nesneler içerir. Başka bir deyişle, Map arayüzünün iç arayüzü olan bu tür anahtar ve tutanak bilgilerini sarmalar; anahtar veya tutanak bilgilerine dair bir şeyler yapmak istediğimizde, entrySet iletisinin döndürdüğü kümenin elemanlarına uygun iletileri göndermemiz gerekir. Bu iletiler, anahtarın sorgulanması (getKey) tutanak bilgisinin sorgulanıp değiştirilmesi (getValue ve setValue) ve eşitlik denetimi (equals) ile kıyım fonksiyonundan (hashCode) ibarettir. Döndürülen küme nesnesinin eşlem nesnesi ile aynı altyapıyı kullandığı unutulmamalıdır. Yani, entrySet'in döndürdüğü küme yoluyla güncellenen bilgiler eşleme yansıyacaktır; benzer şekilde, küme tutacağı eşlem tutacağı ile yapılan değişiklikleri görebilecektir.

Son olarak göz atacağımız iletiler, Object sınıfından tanıdık gelecek olan equals ve hashCode. Sırasıyla, eşitlik denetimi ve kıyım fonksiyonuna karşılık gelen bu iletilerin, Map arayüzüne konulmasının nedeni, geliştiricinin yeni bir eşlem sınıfı gerçekleştirimi sunmak istemesi halinde dikkatsizlikle bu iletileri es geçmesinin önüne geçmektir. Diğer veri kapları gibi eşlemlerin de eşitlik denetimlerinin kap içeriği göz önüne alınarak yapılması equals iletisinin gerçekleştirilmesini zorunlu kılmaktadır. Elemanların belki de hepsini dolaşarak tamamlanacak bu pahalı işlemin maliyetinin düşürülmesi içinse kıyım fonksiyonundan yararlanılması olasılığı hashCode iletisinin gerçekleştirilmesini dikte etmektedir; ne de olsa, kıyım değerleri farklı olan iki nesnenin eşit olması olanaklı değildir.🔎

Bu noktada, önceki paragrafta andığımız iletilerin eşlemleri ilgilendirdiği, anahtar ve tutanak türleri için herhangi bir zorunluluk olmadığı vurgulanmalıdır. Ne var ki; eşlem yapısının arayüzünde yer alan iletilerin gerçekleştirimleri, alternatif bir yöntem olmadığı savlanamasa da, yüksek olasılıkla anahtar ve tutacak bilgilerinin eşitlik denetiminden yararlanarak işlerini göreceklerdir. Bir ihtimal eşlem yapısının gerçekleştirimcisi, eşitlik denetimini anahtar/tutacak bilgisinin kıyım fonksiyonundan yararlanarak kısa yoldan bitirmek isteyecektir. Dolayısıyla, eşlem yapısına konulacak nesnelerin sınıflarında equals ve hashCode iletilerinin gerçekleştirilip gerçekleştirilmeyeceği dikkatle düşünülmeli ve karara bağlanmalıdır.

Map arayüzündeki kimi iletiler, altyapıda kullanılan veri yapısından bağımsız bir biçimde gerçekleştirilebilir. Örneğin, get iletisi eşlemdeki entrySet'in döndürdüğü anahtar-tutanak çiftlerinin gezilerek eşitlik denetimi yapılmasıyla gerçekleştirilebilir. Bunun, farklı sınıflarda tekrarlanmaktansa, ortak bir üst sınıfa (AbstractMap) konulması ve eşlem sınıflarının (HahsMap, TreeMap, vd.) bu sınıftan kalıtlayarak gerçekleştirilmesi kodun yeniden kullanımını artıracaktır. Eşlem gerçekleştirimcisinin daha uygun bir çözümü olması durumunda ise, ilişkin metot ezilerek istenen yapılabilir.

HashMap ve LinkedHashMap Sınıfları


Eşlemlerin bir gerçekleştirim yöntemi, indis ile erişimin yüksek performansından yararlanmak amacıyla altyapıda doğrudan erişimli bir yapı kullanır. Amaç, eşleme gönderilen iletilerin arka plandaki yapı üzerinde uygulanan indis işlemleri haline dönüştürülmesi ve doğrudan erişimin keyfini sürmektir. Ancak, bu söylendiği kadar kolay gerçekleştirilemez; anahtar değerlerinin türü ve alabileceği değerler işi zora sokabilir. Plaka kodu→İl adı örneğimizde işimiz çok basitti ve bundan dolayı dizinin kullanılması düşünülebilir demiştik. Ne var ki, anahtar değerlerinin çok daha geniş ve seyrek bir aralığa dağılması veya tamsayı olmaması söz konusu olabilir. Mesela, farklı yıllarda kaydolmuş öğrencilerin aldığı bir derse dair No→Ad eşlemelerini tutan diziyi düşünün. 8-10 basamaklı bir tamsayı olan öğrenci numarası ve bu numaraların değişik yıllara dağılmış olması, numaradan indise geçişi zorlaştıracaktır. Peki ya, öğrenci numaraları tamsayı olarak değil de String olarak temsil ediliyor olsaydı? Ya da, sözcükler ve anlamları arasındaki eşlemeleri tutmak isteseydik?

Bir önceki paragrafta yöneltilen soruların yanıtı, anahtar değerlerin uygun bir şekilde kıyılıp tamsayıya çevrilmesinden geçer. Yapmamız gereken, önce anahtar değeri hashCode iletisini göndererek kıymak ve elde edilen tamsayıyı arka plandaki doğrudan erişimli yapıya indisli erişimde kullanmaktır. İşin püf noktası olan kıyım fonksiyonunu düzgün gerçekleştirirsek, kullandığımız yöntem neredeyse dizi veya Vector içindeki bir elemana erişim kadar hızlı olacaktır. Kıyım fonksiyonunun kötü olması ise, bir o kadar yavaş bir erişim zamanımız olacağı anlamına gelir. Dolayısıyla, mümkünse, işi bir bilene bırakmakta yarar vardır. Java'da bu yüce makam, HashMap sınıfıdır.

HashMap nesnelerinin performansını etkileyen iki parametre vardır: sığa ve doluluk oranı. Bunlar, sırasıyla, eşlem içeriğinin tutulduğu doğrudan erişimli yapının uzunluğunu ve eşlemdeki anahtar-tutanak çifti sayısının sığaya oranını bildirir. Eşleme eklenmek istenen bir anahtar-tutanak çifti, anahtar değerinin kıyılarak 0 ila sığa-1 arasında bir tamsayıya dönüştürülmesi sonrasında yapının ilişkin konumuna, ki bu konumlara kova denir, eklenecektir. Bu kovanın boş olmaması durumunda ise, anahtar-tutanak çifti daha önceden kovaya konulmuş olan diğer eşlemelerin bulunduğu bir listeye eklenir. Sorgulama ve silme işlemleri de işe anahtar bilgisini kıyarak başlar. Bunun takiben, altyapıdaki doğrudan erişimli yapının ilişkin kovasına bakılır. Söz konusu kova boşsa, her iki işlem de null döndürür. Aksi takdirde, kovada tutulan eşlemelerin bulunduğu listenin aranması sonrasında sonuç döndürülür. Kovanın boş olmaması, eşlemin anahtara dair bilgi içerdiği anlamını taşımaz; kovada yer alan eşlemeler aynı kıyım değerine sahip farklı anahtar değerlerine ilişkin eşlemeler de olabilir.

Zaman içinde ekleme yapıldıkça eşlem dolacak, hem altyapıda kullanılan doğrudan erişimli yapı kalabalıklaşacak hem de kovalar içindeki listeler uzayacaktır. Bu; ekleme, sorgulama ve silme için harcanan zamanların uzayacağı anlamına gelir. Böyle bir durumun oluşmasını engellemek adına HashMap, doluluk oranının belli bir değeri aşması üzerine doğrudan erişimli yapıyı büyütür ve içeriği yeniden düzenler. Oldukça pahalı bir işlem olması nedeniyle, yeniden düzenleme sayısının en aza indirilmesi performans açısından yararlı olacaktır. Bunun için, HashMap nesnesinin yaratılması sırasında sığa ve doluluk oranı parametrelerinin probleme uygun bir biçimde seçilmesi gerekir.

HashMap nesneleri dört yapıcıdan biri kullanılarak yaratılabilir. Bunlardan int ve float argüman alanı, nesneyi istenen ilk sığa ve doluluk oranına sahip olacak şekilde yaratır. Yapıcıya sağlanmayıp işlem sırasında çıkarsanamaması durumunda doluluk oranı 0.75, ilk sığa ise 16 olarak kabul edilir. Dolayısıyla, tek int argüman bekleyen yapıcının kullanılması durumunda, yaratılan eşlem argümanda geçirilen büyüklükte bir ilk sığaya ve 0.75 doluluk oranına sahip olacaktır. Varsayılan yapıcının kullanılması halinde ise, 16 ilk sığalı ve doluluk oranı 0.75 olan bir eşlem yaratılacaktır. Her üç durumda da yaratılan eşlem boş olacaktır. Nesnemizin içeriğe sahip olarak yaratılmasını istiyorsak, yapmamız gereken Map arayüzünü destekleyen bir sınıfın nesnesinin argüman geçirildiği yapıcıyı kullanmaktır. Ancak, şunu unutmayın: argümanındaki eşlem ile aynı sayıda eşlemeye sahip yaratılan yeni nesne doluluk oranını miras almayacak, 0.75 olarak kabul edecektir.

HashMap sınıfının akılda tutulması gereken bir özelliği, eşlem içeriğine dair görüntülerin (entrySet, keySet ve values) anahtar-tutanak çiftlerini herhangi bir sırada vermiyor olmasıdır. Hatta; doluluk oranının aşılması sonrasında yeniden düzenlenen eşlemlerin görüntüleri düzenleme öncesindekiyle alakalı olmayabilir. Bunu, aşağıdaki örneğin işaretli satırındaki örtük toString kullanımından da görebiliriz. Üretilen çıktıda ne ekleme zamanı, ne son erişim zamanı, ne de anahtar bilgileri arasındaki bir sıralama ölçütü dikkate alınmaktadır.

Eşlemler.java
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Eşlemler {
  public static void main(String[] ksa) {
    Map<String, String> sözlük = new HashMap<>();
    sözlüğüDoldur(sözlük);
    System.out.println("Sözlük içeriği: " + sözlük);
    ...
  } // void main(String[]) sonu

  private static void sözlüğüDoldur(Map<String, String> sözlük) {
    Scanner grdKnlı = new Scanner(System.in);
    for (;;) {
      System.out.print("Yeni bir sözcük giriniz: ");
      String sözcük = grdKnlı.nextLine();
      if (sözcük.toLowerCase().equals("bitti")) break;
      System.out.print("Anlam: ");
      String anlam = grdKnlı.nextLine();
      sözlük.put(sözcük, anlam);
    } 
  } // void sözlüğüDoldur(Map<String, String>) sonu 
  ...
} // Eşlemler sınıfının sonu
Derdimizin çaresi, sergilenen bu kaotik davranışı değiştiren LinkedHashMap sınıfındadır. HashMap'ten kalıtlayan bu sınıf, doğrudan erişimli yapıya ek olarak, altyapıda tuttuğu çift bağlaçlı bir liste ile eşlem içeriğini yapıcı çağrısı sırasında belirlenen iki ölçütten birine göre sıralı tutarak üretilen çıktıyı beklentinize göre oluşturacaktır. Aksi söylenmediği takdirde ekleme sırasında üretilecek olan çıktı, ilk sığa ve doluluk oranına ek olarak sağlanacak üçüncü bir argümanla en son erişim sırasına göre de oluşturulabilir. Örneğin, aşağıdaki kod parçasının işaretli satırındaki yapıcı çağrısı, sözlük2 tarafından temsil edilen eşlemin en son erişilen anahtar-tutanak çiftleri başta gelecek şekilde görüntülenmesini sağlar.
...
import java.util.LinkedHashMap;

public class Eşlemler {
  public static void main(String[] ksa) {
    Map<String, String> sözlük2 = new LinkedHashMap<>(100, 0.80, true);
    sözlüğüDoldur(sözlük2);
    // sözlük2'yi kullan...
    System.out.println("Sözlük içeriği: " + sözlük2);
    ...
  } // void main(String[]) sonu
  ...
} // Eşlemler sınıfının sonu
Diğer yapıcıları HashMap'tekilerle aynı şekilde çalışan LinkedHashMap sınıfı, HashMap'te olduğu gibi Map arayüzündekinden başka bir işlevsellik sunmaz.

Hashtable Sınıfı


Buraya kadar gelip de pes etmeyenlere müjde, Hashtable sınıfı HashMap ile neredeyse tamamıyla aynıdır. J2SDK 1.2 öncesinde tanımlanmış olan bu sınıfın HashMap'ten farklı olarak desteklediği kimi iletiler herhangi bir ekstra işlevsellik katmaz. Ancak, sakın ola ki, bu iki sınıfın her zaman birbirlerinin yerine kullanılabileceğini sanmayın. Bunun başlıca üç sebebi vardır:
  1. Fazladan bir işlev katmamakla beraber Hashtable tarafından sağlanan J2SDK 1.2 öncesinden kalma contains, elements ve keys iletileri HashMap nesnelerine gönderilemez. [Eski kodlarınızı elden geçirirken bu iletileri, sırasıyla, containsValue, values ve keySet iletileri ile değiştirmenizi tavsiye ederim.]
  2. Diğer eşlem sınıflarının aksine, Hashtable anahtar değeri olarak null değerinin kullanılmasına izin vermez.
  3. Diğer eşlem sınıflarının aksine, Hashtable çok izlekli kullanım düşünülerek yazılmıştır. Yani, HashMap [ve TreeMap] nesnelerinin farklı izleklerden aynı anda kullanılmak istenmeleri durumunda programcının yapının tutarlılığını korumak adına eşgüdüm kodu yazması gerekirken, Hashtable için böyle bir şey söz konusu değildir.

  1. Çok özel eleman türü ve dağılımı istemeleri nedeniyle O(n) performans sergileseler de kimi algoritmaların bu gibi genel kullanımlarda zikredilmeleri mantıklı değildir.
  2. Bu aslında o kadar da kısıtlayıcı bir şey değil. Anahtarın birden çok değer ile ilişkilendirilmesini istiyorsanız, tutanak türü kısmında List veya Set gibi bir kap türünün belirtilmesi yeterli olacaktır.