Java'daki HashMap ve Map nesneleri arasındaki fark nedir?


349

Oluşturduğum aşağıdaki haritalar arasındaki fark nedir (başka bir soruda, insanlar bunları dönüşümlü olarak kullanarak cevap verdiler ve farklı olup olmadıklarını / nasıl olduklarını merak ediyorum):

HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();

HashMap kullanarak uyguladığınızı ve Mary'nin Map kullandığını varsayalım. Derleyecek mi?
GilbertS

Yanıtlar:


446

Nesneler arasında fark yoktur; Eğer bir var HashMap<String, Object>her iki durumda da. Arayüzde nesneye sahip olduğunuz bir fark var. İlk durumda, arayüz ise HashMap<String, Object>ikinci sırada Map<String, Object>. Ancak altta yatan nesne aynı.

Kullanmanın avantajı Map<String, Object>, altta yatan nesneyi, onu kullanan herhangi bir kodla sözleşmenizi bozmadan farklı türde bir harita olarak değiştirebilmenizdir. Bunu beyan ederseniz HashMap<String, Object>, temeldeki uygulamayı değiştirmek istiyorsanız sözleşmenizi değiştirmeniz gerekir.


Örnek: Diyelim ki bu sınıfı yazıyorum:

class Foo {
    private HashMap<String, Object> things;
    private HashMap<String, Object> moreThings;

    protected HashMap<String, Object> getThings() {
        return this.things;
    }

    protected HashMap<String, Object> getMoreThings() {
        return this.moreThings;
    }

    public Foo() {
        this.things = new HashMap<String, Object>();
        this.moreThings = new HashMap<String, Object>();
    }

    // ...more...
}

Sınıf, alt sınıflarla paylaştığı (erişimci yöntemleri aracılığıyla) dize-> nesnesinin birkaç dahili haritasına sahiptir. Diyelim ki HashMapbaşlamak için s ile yazıyorum çünkü sınıf yazarken bu uygun yapı olduğunu düşünüyorum.

Daha sonra, Mary kodun alt sınıfını yazar. Her ikisiyle de yapması gereken bir şey var thingsve moreThingsdoğal olarak bunu ortak bir yöntemle koyuyor ve yöntemini tanımlarken getThings/ kullandığım aynı türü kullanıyor getMoreThings:

class SpecialFoo extends Foo {
    private void doSomething(HashMap<String, Object> t) {
        // ...
    }

    public void whatever() {
        this.doSomething(this.getThings());
        this.doSomething(this.getMoreThings());
    }

    // ...more...
}

Daha sonra ben kullanırsanız aslında, daha iyi olduğuna karar TreeMapyerine HashMapde Foo. Ben güncelleme Foo, değişen HashMapiçin TreeMap. Şimdi, SpecialFooartık derlemiyor, çünkü sözleşmeyi çiğnedim: s Foosağladığını söylerdim HashMap, ama şimdi TreeMapsyerine veriyor . Bu yüzden SpecialFooşimdi düzeltmeliyiz (ve bu tür bir şey bir kod tabanından dalgalanabilir).

Uygulamamın bir HashMap(ve bu olduğu) kullandığını paylaşmak için gerçekten iyi bir nedenim olmadıkça , yapmam gereken şey beyan etmekti getThingsve bundan daha spesifik olmadan getMoreThingsgeri dönmekti Map<String, Object>. Aslında, hatta içinde, başka bir şey yapmak için iyi bir neden engelleme FooMuhtemelen ilan etmeli thingsve moreThingsolarak Mapdeğil, HashMap/ TreeMap:

class Foo {
    private Map<String, Object> things;             // <== Changed
    private Map<String, Object> moreThings;         // <== Changed

    protected Map<String, Object> getThings() {     // <== Changed
        return this.things;
    }

    protected Map<String, Object> getMoreThings() { // <== Changed
        return this.moreThings;
    }

    public Foo() {
        this.things = new HashMap<String, Object>();
        this.moreThings = new HashMap<String, Object>();
    }

    // ...more...
}

Şimdi Map<String, Object>yapabildiğim her yerde nasıl kullandığımı , yalnızca gerçek nesneleri oluşturduğumda özel olduğumu unutmayın.

Bunu yapsaydım, Mary bunu yapardı:

class SpecialFoo extends Foo {
    private void doSomething(Map<String, Object> t) { // <== Changed
        // ...
    }

    public void whatever() {
        this.doSomething(this.getThings());
        this.doSomething(this.getMoreThings());
    }
}

... ve değişen Fooolmazdı SpecialFoodurdurma derleme.

Arayüzler (ve temel sınıflar) , gerekli olduğu kadar değişiklik yapmak için esnekliğimizi kapakların altında tutarak , sadece gerektiği kadar açığa çıkarmamıza izin verir . Genel olarak, referanslarımızın mümkün olduğunca temel olmasını istiyoruz. Bunun a olduğunu bilmemize gerek yoksa HashMap, sadece a deyin Map.

Bu kör bir kural değildir, ancak genel olarak, en genel arayüze kodlama daha spesifik bir şeyi kodlamaktan daha az kırılgan olacaktır. Bunu hatırlasaydım, FooMary'yi başarısızlık için ayarlayan bir şey yaratmazdım SpecialFoo. Eğer Mary o zaman berbat halde olduğunu hatırlamıştı Foo, o ile onun özel yöntem ilan olurdu Mapyerine HashMapve benim değişen Foo'ın sözleşmesini onun kodunu etkiledi olmazdı.

Bazen bunu yapamazsın, bazen spesifik olmalısın. Ancak olmanız için bir nedeniniz yoksa, en az spesifik arayüze yöneliniz.


56

Harita , HashMap'in uyguladığı bir arabirimdir . Fark, ikinci uygulamada HashMap'e referansınızın yalnızca Harita arayüzünde tanımlanan işlevlerin kullanımına izin verirken, ilki HashMap'te (Harita arayüzünü içeren) herhangi bir genel fonksiyonun kullanımına izin verecektir.

Sun'ın arayüz eğitimini okursanız muhtemelen daha mantıklı olacaktır


Kabul ediyorum: ilk = HashMap <String, Object> map = new HashMap <String, Object> ();
OneWorld

Bir Listenin ArrayList olarak ne sıklıkla uygulandığına benzer
Gerard

26

resim açıklamasını buraya girin

Harita aşağıdaki uygulamalara sahiptir:

  1. HashMap Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. Ağaç Haritası Map m = new TreeMap();

  4. WeakHashMap Map m = new WeakHashMap();

Bir yöntem oluşturduğunuzu varsayalım (bu yalnızca sözde koddur).

public void HashMap getMap(){
   return map;
}

Proje gereksinimlerinizde değişiklik olduğunu varsayalım:

  1. Yöntem harita içeriğini döndürmelidir - Geri dönmesi gerekir HashMap.
  2. Yöntem, harita anahtarını ekleme sırasında döndürmelidir - Dönüş türünü değiştirmeniz HashMapgerekir LinkedHashMap.
  3. Yöntem, harita anahtarını sıralı olarak döndürmelidir - Dönüş türünü değiştirmeniz LinkedHashMapgerekir TreeMap.

Yönteminiz Maparabirimi uygulayan bir şey yerine belirli sınıflar döndürüyorsa , getMap()her seferinde yöntemin dönüş türünü değiştirmeniz gerekir .

Ancak Java'nın polimorfizm özelliğini kullanırsanız ve belirli sınıfları döndürmek yerine arabirimi kullanırsanız, Mapkod yeniden kullanılabilirliğini artırır ve gereksinim değişikliklerinin etkisini azaltır.


17

Ben sadece bunu kabul edilen cevaba bir yorum olarak yapacaktım ama çok korkak oldu (satır sonları olmamaktan nefret ediyorum)

ah, aradaki fark, genel olarak, Harita'nın kendisiyle ilişkili belirli yöntemleri olmasıdır. ancak HashMap gibi farklı yollar veya bir harita oluşturmak vardır ve bu farklı yollar tüm haritaların sahip olmadığı benzersiz yöntemler sağlar.

Kesinlikle - ve her zaman mümkün olan en genel arayüzü kullanmak istersiniz. ArrayList ve LinkedList'i düşünün. Bunları nasıl kullandığınız konusunda büyük fark vardır, ancak "Liste" kullanırsanız, bunlar arasında kolayca geçiş yapabilirsiniz.

Aslında, başlatıcıyı sağ tarafını daha dinamik bir ifadeyle değiştirebilirsiniz. böyle bir şeye ne dersin:

List collection;
if(keepSorted)
    collection=new LinkedList();
else
    collection=new ArrayList();

Bu şekilde, koleksiyonu bir sıralama türüyle dolduracaksanız bağlantılı bir liste kullanırsınız (bir dizi listesine ekleme sıralama suçtur.) Ancak, diziyi sıralamanıza gerek yoktur ve sadece ekliyorsanız, ArrayList kullanırsınız (diğer işlemler için daha verimli).

Bu oldukça büyük bir gerginlik çünkü koleksiyonlar en iyi örnek değil, ancak OO tasarımında en önemli kavramlardan biri, aynı cepheye sahip farklı nesnelere erişmek için arayüz cephesini kullanıyor.

Yoruma verilen yanıtı düzenleyin:

Aşağıdaki harita yorumunuza gelince, "Harita" arayüzünü kullanmak Evet, koleksiyonu Haritadan HashMap'e geri göndermediğiniz sürece (yalnızca TAMAMEN amacı bozguna uğratır) sizi yalnızca bu yöntemlerle sınırlar.

Genellikle yapacağınız şey bir nesne oluşturmak ve onu belirli bir tür (HashMap) kullanarak doldurmaktır, bir çeşit "create" veya "initialize" yöntemiyle, ancak bu yöntem olması gerekmeyen bir "Map" döndürür artık HashMap olarak değiştiriliyor.

Bu arada kullanmanız gerekiyorsa, muhtemelen yanlış arayüzü kullanıyorsunuz veya kodunuz yeterince iyi yapılandırılmamış. Kodunuzun bir bölümünün bunu "HashMap" olarak ele almasının kabul edilirken, diğerinin "Harita" olarak ele almasının kabul edilebilir, ancak bu "aşağı" akmalıdır. asla döküm yapmamanız için.

Ayrıca, arabirimler tarafından gösterilen rollerin yarı temiz yönüne de dikkat edin. LinkedList iyi bir yığın veya sıra yapar, ArrayList iyi bir yığın yapar ancak korkunç bir sıra yapar (yine kaldırma işlemi tüm listenin kaymasına neden olur), bu yüzden LinkedList sıra arayüzünü uygular, ArrayList bunu yapmaz.


ama bu örnekte, ben sadece genel List sınıfından yöntemleri almak, değil mi? bir LinkedList () veya ArrayList () yapmamdan bağımsız olarak? sadece ekleme sıralama kullanırsanız (hangi LinkedList ve ArrayList miras tarafından almak listesi için bir yöntem olması gerektiğini hayal), LinkedList yol daha hızlı çalışır?
Tony Stark

Harita <string, string> m = yeni HashMap <string, string> () My Map m HashMap için özel yöntemleri kullanabilir ya da değil kullanabilirsiniz dediğimde ne aradığımı tahmin ediyorum. Bence yapamaz mıyım?
Tony Stark

ah, bekleyin, hayır, Haritam m yukarıdan HashMap yöntemlerine sahip olmalıdır.
Tony Stark

yani temelde Harita 'arayüz anlamda' kullanarak tek diken bir harita gerektiren bir yöntem varsa, ben herhangi bir harita türü bu yöntemde çalışacağını garanti 'dir. ama eğer bir hashmap kullansaydım, metodun sadece hashmaps ile çalıştığını söylüyorum. veya başka bir deyişle, yöntemim yalnızca Map sınıfında tanımlanan ancak Map'u genişleten diğer Sınıflar tarafından miras alınan yöntemleri kullanır.
Tony Stark

Yukarıda bahsettiğiniz dikmenin yanı sıra, List'i kullanmak, çalışma zamanına kadar hangi Liste türünü istediğime karar vermem gerekmediği anlamına gelirken, arayüz şey yoksa, derlemeden ve çalıştırmadan önce bir tane seçmem gerekir
Tony Stark

12

TJ Crowder ve Adamski'nin belirttiği gibi, bir referans bir arayüze, diğeri arayüzün belirli bir uygulamasına ilişkindir. Joshua Block'a göre, her zaman arayüzlere kod yazmayı denemelisiniz, temel uygulamadaki değişiklikleri daha iyi ele almanıza izin vermelisiniz - yani HashMap aniden çözümünüz için ideal değilse ve harita uygulamasını değiştirmeniz gerekiyorsa, yine de Harita'yı kullanabilirsiniz. arayüzünü ve örnekleme türünü değiştirin.


8

İkinci örneğinizde "harita" referansı, tür (ve diğer türler ) Maptarafından uygulanan bir arabirimdir . Bu arayüz bir olan sözleşme nesnesi çeşitli işlemleri değerlere anahtarlarını eşler ve desteklediğini söyleyerek (örneğin , ). Diyor uygulanması hakkında hiçbir şey arasında (bu durumda a'da ).HashMapMapputgetMapHashMap

İkinci yaklaşım genellikle, belirli harita uygulamasını Mapveya API tanımını kullanan yöntemlere maruz bırakmak istemediğiniz için genellikle tercih edilir .


8

Harita statik harita türüdür , HashMap ise dinamik harita türüdür . Bu, derleyicinin harita nesnesine Harita türünden biri olarak davranacağı anlamına gelir, çalışma zamanında olsa da, herhangi bir alt türüne işaret edebilir.

Bu uygulamalar yerine arabirimlere karşı programlama pratiğinin esnek kalmanın ek bir yararı vardır: Örneğin, haritanın bir alt türü (ör. LinkedHashMap) olduğu sürece, çalışma zamanında dinamik harita türünü değiştirebilir ve haritanın davranışını değiştirebilirsiniz. Sinek.

İyi bir kural, API düzeyinde olabildiğince soyut kalmaktır: Örneğin, programladığınız bir yöntemin haritalarda çalışması gerekiyorsa, bir parametreyi daha katı (daha az soyut olduğu için) HashMap türü yerine Harita olarak bildirmek yeterlidir . Bu şekilde, API'nızın tüketicisi, yönteminize ne tür bir Harita uygulaması uygulamak istedikleri konusunda esnek olabilir.


4

En çok oy alan yanıtı ve "daha genel, daha iyi" vurgulayarak yukarıda birçok cevap ekleyerek, biraz daha kazmak istiyorum.

Mapyapısal sözleşmedir HashMap, farklı gerçek problemlerle başa çıkmak için kendi yöntemlerini sağlayan bir uygulamadır: endeks nasıl hesaplanır, kapasite nedir ve nasıl artırılır, nasıl eklenir, endeksin benzersiz tutulması, vb.

Kaynak kodunu inceleyelim:

İçinde Mapyöntemi var containsKey(Object key):

boolean containsKey(Object key);

javadoc:

boolean java.util.Map.containsValue (Nesne değeri)

Bu harita bir veya daha fazla anahtarı belirtilen değere eşlerse true değerini döndürür. Daha resmi olarak, yalnızca ve bu harita vböyle bir değere en az bir eşleme içeriyorsa true değerini döndürür (value==null ? v==null : value.equals(v)). Bu işlem, Harita arabiriminin çoğu uygulaması için muhtemelen harita boyutunda zaman çizgisi gerektirecektir.

Parametreler: değeri

Bu haritadaki varlığı test edilecek değer

İade: true

bu harita belirtilen anahtarlarla bir veya daha fazla anahtarı eşlerse

valueThrows:

ClassCastException - değer bu harita için uygun olmayan bir türdeyse (isteğe bağlı)

NullPointerException - belirtilen değer null ise ve bu harita null değerlere izin vermiyorsa (isteğe bağlı)

Uygulamalarının uygulanmasını gerektirir, ancak "nasıl yapılır" özgürlüğünde, yalnızca doğru şekilde döndüğünden emin olmak için.

İçinde HashMap:

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

HashMapBu haritanın anahtarı içerip içermediğini test etmek için hashcode kullandığı ortaya çıkıyor . Yani hash algoritmasının faydası var.


3

Aynı haritaları siz oluşturursunuz.

Ancak farkı ne zaman kullanacağınızı doldurabilirsiniz. İlk durumda özel HashMap yöntemlerini kullanabileceksiniz (ancak kimsenin gerçekten yararlı olduğunu hatırlamıyorum) ve bir HashMap parametresi olarak geçirebileceksiniz:

public void foo (HashMap<String, Object) { ... }

...

HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;

foo (m1);
foo ((HashMap<String, Object>)m2); 

3

Harita arayüzdür ve Hashmap Harita Arayüzü uygulayan bir sınıftır


1

Harita Arayüz ve Hashmap bunu uygulayan sınıftır.

Yani bu uygulamada aynı nesneleri yaratıyorsunuz


0

HashMap Map'in bir uygulamasıdır, bu yüzden aynıdır ancak referans kılavuzunda gördüğüm gibi "clone ()" yöntemine sahiptir))


0
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();  

Her şeyden önce Mapbunun gibi farklı uygulama olan bir arayüz - HashMap, TreeHashMap, LinkedHashMapvb Arayüz uygulayan sınıf için süper bir sınıf gibi çalışır. Uygular o Oop en kuralına herhangi bir somut sınıfını göre Yani Mapbir olduğunu Mapda. Bu, herhangi HashMapbir Maptür değişkeni, herhangi bir döküm türü olmadan bir tür değişkenine atayabilir / koyabiliriz .

Bu durumda map1, map2herhangi bir döküm veya veri kaybı olmadan atayabiliriz -

map2 = map1
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.