Java Haritası Koleksiyonu neden genişletmiyor?


146

Ben Map<?,?>değil bir gerçeği ile şaşırdım Collection<?>.

Eğer böyle ilan edilirse çok mantıklı olacağını düşündüm:

public interface Map<K,V> extends Collection<Map.Entry<K,V>>

Sonuçta, a Map<K,V>bir koleksiyon Map.Entry<K,V>, değil mi?

Peki bunun böyle uygulanmamasının iyi bir nedeni var mı?


Bir çoğu yetkili cevap için Cletus sayesinde, ama hala neden, zaten bir görüntüleyebilirsiniz merak ediyorum Map<K,V>olarak Set<Map.Entries<K,V>>(via entrySet()), sadece yerine o arayüze uzatmaz.

Bir Eğer Mapbir olduğunu Collection, elementler nelerdir? Tek makul cevap "Anahtar / değer çiftleri" dir

Kesinlikle, interface Map<K,V> extends Set<Map.Entry<K,V>>harika olurdu!

ancak bu çok sınırlı (ve özellikle yararlı olmayan) bir Mapsoyutlama sağlar.

Ancak durum buysa, neden entrySetarabirim tarafından belirtilir? Bir şekilde yararlı olmalı (ve bu pozisyon için tartışmanın kolay olduğunu düşünüyorum!).

Belirli bir anahtarın hangi değerle eşleştiğini soramazsınız veya belirli bir anahtarın girdisini hangi değerle eşlendiğini bilmeden silebilirsiniz.

Bunun için tek şey olduğunu söylemiyorum Map! It can ve gerektiği (dışındaki tüm diğer yöntemleri tutmak entrySetartık gereksiz olan)!


1
event Set <Map.Entry <K, V >> aslında
Maurice Perry

3
Sadece öyle değil. Mantıksal bir sonuç olmaktan ziyade (Tasarım SSS bölümünde açıklandığı gibi) bir düşünceye dayanır. Alternatif bir yaklaşım için, Stepanov'un kapsamlı ve parlak analizine dayanan C ++ STL ( sgi.com/tech/stl/table_of_contents.html ) içindeki konteyner tasarımına bakın .
beldaz

Yanıtlar:


123

Gönderen Java Koleksiyonları API Tasarım SSS :

Harita neden Koleksiyonu genişletmiyor?

Bu tasarım gereğiydi. Eşlemelerin koleksiyon olmadığını ve koleksiyonların eşleme olmadığını düşünüyoruz. Bu nedenle, Harita'nın Koleksiyon arayüzünü genişletmesi (veya tersi) çok az mantıklıdır.

Bir Harita Koleksiyonsa, öğeler nelerdir? Tek makul cevap "Anahtar / değer çiftleri" dir, ancak bu çok sınırlı (ve özellikle yararlı olmayan) bir Harita soyutlaması sağlar. Belirli bir anahtarın hangi değerle eşleştiğini soramazsınız veya belirli bir anahtarın girdisini hangi değerle eşlendiğini bilmeden silebilirsiniz.

Harita'yı genişletmek için toplama yapılabilir, ancak bu şu soruyu gündeme getirir: anahtarlar nelerdir? Gerçekten tatmin edici bir cevap yok ve birini zorlamak doğal olmayan bir arayüze yol açıyor.

Haritalar Koleksiyonlar (anahtarlar, değerler veya çiftler) olarak görüntülenebilir ve bu gerçek Haritalar'daki üç "Koleksiyon görünümü işlemleri" ne (keySet, entrySet ve değerler) yansıtılır. Prensip olarak, bir Listeyi bir harita eşleme indeksi olarak öğelere görüntülemek mümkün olsa da, bu bir öğeyi Listeden silmenin silinen öğeden önceki her öğeyle ilişkili Anahtarı değiştirdiği kötü bir özelliğe sahiptir. Bu yüzden Listeler üzerinde harita görüntüleme işlemimiz yoktur.

Güncelleme: Bence alıntı soruların çoğuna cevap veriyor. Özellikle yararlı bir soyutlama olmadığı bir giriş koleksiyonu hakkındaki kısmı vurgulamakta fayda var. Örneğin:

Set<Map.Entry<String,String>>

izin verir:

set.add(entry("hello", "world"));
set.add(entry("hello", "world 2");

( entry()birMap.Entry örnek )

Maps benzersiz anahtarlar gerektirir, böylece bu bunu ihlal eder. Veya bir Setgirişe benzersiz anahtarlar uygularsanız , bu Setgenel anlamda bir anahtar değildir . Daha Setfazla kısıtlama ile bir.

Tartışmalı bir şekilde equals()/ hashCode()ilişki için Map.Entrysadece anahtar olduğunu söyleyebiliriz ama hatta sorunları vardır. Daha da önemlisi, gerçekten herhangi bir değer katıyor mu? Köşe vakalarına bakmaya başladığınızda bu soyutlamanın bozulduğunu görebilirsiniz.

HashSetAslında bir olarak uygulandığını HashMap, başka bir şekilde değil, dikkat çekiyor . Bu sadece bir uygulama detayıdır, ancak yine de ilginçtir.

Var entrySet()olmanın ana nedeni, geçişi basitleştirmektir, böylece anahtarları geçmeniz ve ardından anahtar araması yapmanız gerekmez. A'nın girişler Mapolması gerektiğine dair prima facie kanıtı olarak kabul etmeyin Set(imho).


1
Güncelleme çok ikna edicidir, ancak gerçekten tarafından döndürülen Set görünümü entrySet()desteklemez remove, desteklemez add(muhtemelen atar UnsupportedException). Seni anlıyorum, ama yine de, ben OP'ın noktaya bakın Yani, çok .. (Kendi görüş kendi cevap belirtildiği gibi ..)
Enno Shioji

1
Zwei iyi bir noktaya değiniyor. Tüm bu komplikasyonlar add, entrySet()görünümün nasıl olduğu gibi ele alınabilir : bazı işlemlere izin verin ve diğerlerine izin vermeyin. Doğal bir yanıt, elbette, "Ne tür Setdesteklemiyor add?" - Peki, eğer böyle bir davranış kabul edilebilirse entrySet(), neden kabul edilemez this? Dedi ki, ben am sadece iyi API / OOP tasarım kılan kendi anlayış zenginleştirmek eğer düşünce bir kez, ama yine de, bu, daha fazla tartışmaya layık olduğunu düşünüyorum olarak çoğunlukla bu da iyi bir fikir değil neden zaten ikna etti.
poligenelubrikantlar

3
SSS'den alıntı yapılan ilk paragraf komik: "Hissediyoruz ... bu yüzden ... çok mantıklı değil". "Hissediyorum" ve "duyu"
nun

Harita genişletmek için toplama yapılabilir mi? Emin değilim, yazar neden Collectionuzatmayı düşünüyor Map?
overexchange

11

Sorunuzu doğrudan ilgilendiren bir takım cevaplar alırken, biraz geri çekilmenin ve soruya biraz daha genel olarak bakmanın yararlı olabileceğini düşünüyorum. Yani, özellikle Java kütüphanesinin nasıl yazıldığına bakmak ve neden bu şekilde yazıldığına bakmak değil.

Buradaki sorun, kalıtımın sadece bir tür ortaklığı modellenmesidir . Her ikisi de "koleksiyona benzeyen" gibi görünen iki şeyi seçerseniz, muhtemelen ortak olan 8 veya 10 şeyi seçebilirsiniz. Farklı bir "koleksiyon benzeri" şeyler seçerseniz, bunlar ortak 8 veya 10 şey olacak - ancak ilk çiftle aynı 8 veya 10 şey olmayacaklar .

Bir düzine kadar farklı "koleksiyona benzeyen" şeye bakarsanız, hemen hemen her birinin muhtemelen en az bir diğeriyle ortak 8 veya 10 karakteristik bir şeye sahip olacaktır - ancak her birinde paylaşılanlara bakarsanız bunlardan hiçbiri kalmadı.

Bu, kalıtımın (özellikle tek miras) iyi bir modellemediği bir durumdur. Bunların arasında gerçekten koleksiyon olan ve olmayanlar arasında temiz bir ayrım çizgisi yoktur - ancak anlamlı bir Koleksiyon sınıfı tanımlamak istiyorsanız, bazılarını dışarıda bırakmakla sıkışıp kalırsınız. Bunlardan yalnızca birkaçı dışarıda kalırsanız, Koleksiyon sınıfınız yalnızca oldukça seyrek bir arayüz sağlayabilir. Daha fazla dışarıda kalırsanız, daha zengin bir arayüz sunabilirsiniz.

Bazıları temel olarak şunları söyleme seçeneğini de alır: "bu tür bir koleksiyon X işlemini destekler, ancak X'i tanımlayan bir temel sınıftan türeterek, ancak türetilmiş 'X sınıfını kullanmaya çalışmak başarısız olur (ör. , bir istisna atarak).

Bu hala bir sorun bırakıyor: neredeyse hangisini bıraktığınıza ve hangisini yerleştirdiğinize bakılmaksızın, hangi sınıfların içinde ve dışarıda olanlar arasında sert bir çizgi çizmeniz gerekecek. Bu çizgiyi nereden çizerseniz seçin, oldukça benzer bazı şeyler arasında net, oldukça yapay bir ayrım kalacaksınız .


10

Sanırım neden özneldir.

C #, bence Dictionarybir koleksiyonu genişletir veya en azından uygular:

public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, 
    ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, 
    IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback

Pharo Smalltak'ta da:

Collection subclass: #Set
Set subclass: #Dictionary

Ancak bazı yöntemlerle bir asimetri var. Örneğin, collect:will ilişkilendirmeyi (bir girişin eşdeğerini) alırken do:, değerleri alır. keysAndValuesDo:Sözlüğü giriş yoluyla yinelemek için başka bir yöntem sağlarlar . Add:bir ilişki kurar, ancak remove:"gizlenir":

remove: anObject
self shouldNotImplement 

Bu yüzden kesinlikle yapılabilir, ancak sınıf hiyerarşisiyle ilgili başka sorunlara yol açar.

Daha iyisi özneldir.


3

Cletus'un cevabı iyidir, ancak semantik bir yaklaşım eklemek istiyorum. Her ikisini de birleştirmek anlamsızdır, toplama arabirimi aracılığıyla bir anahtar / değer çifti eklediğinizi ve anahtarın zaten var olduğunu düşünün. Harita arayüzü, anahtarla ilişkili yalnızca bir değere izin verir. Ancak, mevcut girişi aynı anahtarla otomatik olarak kaldırırsanız, koleksiyon bir öncekiyle aynı boyuttan sonra eklenir - koleksiyon için çok beklenmedik.


4
Bu nasıl Setçalıştığını ve Set yok uygulamak Collection.
Lii

2

Java koleksiyonları bozuk. İlişki arayüzü eksik. Bu nedenle, Harita, İlişki Seti'ni genişletir. İlişkiler (çoklu haritalar olarak da adlandırılır) benzersiz ad-değer çiftlerine sahiptir. Haritalar (diğer adıyla "İşlevler"), elbette değerlerle eşlenen benzersiz adlara (veya tuşlara) sahiptir. Sekanslar Haritalar'ı genişletir (her tuşun bir tamsayı> 0 olması). Torbalar (veya çoklu setler) Haritaları genişletir (burada her anahtar bir öğedir ve her değer, öğenin torbada kaç kez göründüğüdür).

Bu yapı, bir dizi "koleksiyonun" kesişmesine, birleşmesine vb. Olanak sağlayacaktır. Bu nedenle, hiyerarşi şöyle olmalıdır:

                                Set

                                 |

                              Relation

                                 |

                                Map

                                / \

                             Bag Sequence

Sun / Oracle / Java ppl - lütfen bir dahaki sefere hemen alın. Teşekkürler.


4
Bu hayali arayüzler için API'leri detaylı olarak görmek istiyorum. Anahtarın bir Tamsayı olduğu bir Sıra (aka Liste) istediğinizden emin değilim; bu büyük bir performans isabeti gibi geliyor.
Lawrence Dol

Bu doğru, bir dizi bir listedir - dolayısıyla Dizi Liste'yi genişletir. Buna erişip erişemeyeceğinizi bilmiyorum, ancak ilginç olabilir. Portal.acm.org/citation.cfm?id=1838687.1838705
xagyg

@SoftwareMonkey burada başka bir bağlantı. Son bağlantı, API'nin javadocunun bağlantısıdır. zedlib.sourceforge.net
xagyg

@SoftwareMonkey Buradaki tasarımın asıl endişem olduğunu utanmadan kabul ediyorum. Oracle / Sun'daki guruların halkı optimize edebileceğini varsayıyorum.
xagyg

Katılıyorum. Etki alanının üye olmayan öğelerini kümenize her zaman ekleyemiyorsanız "Harita bir Kümedir" i nasıl tutabilir (@cletus'un cevabındaki örnekte olduğu gibi).
einpoklum

1

İlgili veri yapısına bakarsanız, neden Mapbir parçası olmadığını kolayca tahmin edebilirsiniz Collection. Her biri Collectiontek bir değeri depolar, burada Mapanahtar / değer çiftini depolar. Dolayısıyla Collectionarayüzdeki yöntemler arayüz için uyumsuzdur Map. Mesela Collectionvar add(Object o). Böyle bir uygulama ne olurdu Map. Böyle bir yöntemin olması mantıklı değil Map. Bunun yerine bir put(key,value)yöntemimiz var Map.

Aynı argüman için de geçerli addAll(), remove()ve removeAll()yöntemlerle. Bu nedenle asıl neden, verinin Mapve içinde saklanma biçimindeki farktır Collection. Ayrıca Collectionarayüzü uygulanmış Iterablearayüzü hatırlıyorsanız yani .iterator()yöntemi ile herhangi bir arayüz bize depolanan değerler üzerinde yineleme izin gerekir bir yineleyici dönmelidir Collection. Şimdi böyle bir yöntem neye dönecekti Map? Anahtar yineleyici veya Değer yineleyici? Bu da bir anlam ifade etmiyor.

Bir anahtar ve değer deposu üzerinde yineleme yapabileceğimiz yollar vardır Mapve bu Collectionçerçevenin bir parçasıdır .


There are ways in which we can iterate over keys and values stores in a Map and that is how it is a part of Collection framework.Bunun için bir kod örneği gösterebilir misiniz?
RamenChef

Bu çok iyi açıklanmıştı. Şimdi anladım. Teşekkürler!
Emir Memiç

Uygulanması add(Object o)bir Entry<ClassA, ClassB>nesne eklemek olacaktır . A MapdüşünülebilirCollection of Tuples
LIvanov

0

Kesinlikle, interface Map<K,V> extends Set<Map.Entry<K,V>>harika olurdu!

Aslında, öyleyse implements Map<K,V>, Set<Map.Entry<K,V>>, o zaman hemfikirim. Hatta doğal görünüyor. Ama bu pek iyi çalışmıyor, değil mi? Diyelim ki elimizde HashMap implements Map<K,V>, Set<Map.Entry<K,V>, LinkedHashMap implements Map<K,V>, Set<Map.Entry<K,V>vb ... hepsi iyi, ama eğerentrySet() , kimse bu yöntemi uygulamayı unutamaz ve herhangi bir Harita için inputSet alabileceğinizden emin olabilirsiniz, oysa umuyorsanız, uygulayıcının her iki arabirimi de uyguladığı ...

Sahip olmak istemememin sebebi interface Map<K,V> extends Set<Map.Entry<K,V>>basitçe, çünkü daha fazla yöntem olacak. Sonuçta, bunlar farklı şeyler, değil mi? Ayrıca çok pratik olarak, eğer map.IDE'ye vurursam, görmek istemiyorum .remove(Object obj)ve .remove(Map.Entry<K,V> entry)çünkü yapamıyorum hit ctrl+space, r, returnve onunla yapamam .


Bana göre biri anlamsal olarak, "Bir Harita-Haritada Bir Settir.Girişler" olduğunu iddia edebilirse, o zaman ilgili yöntemi uygulamak rahatsız olurdu; ve eğer kişi bu açıklamayı yapamazsa, o zaman olmazdı - yani rahatsızlıkla ilgili olmalıdır.
einpoklum

0

Map<K,V>şu Set<Map.Entry<K,V>>tarihten beri uzatılmamalıdır :

  • Sen olamaz farklı eklemek Map.Entryaynı aynı anahtarla s Map, ancak
  • Sen olabilir farklı ekleyebilir Map.Entryaynı aynı anahtarla s Set<Map.Entry>.

... bu @cletus'un cevabının ikinci kısmının ghistinin özlü bir ifadesidir.
einpoklum

-2

Düz ve basit. Koleksiyon yalnızca bir Nesne bekleyen bir arayüzken, Harita iki gerektirir.

Collection(Object o);
Map<Object,Object>

Güzel Trinad, cevaplarınız her zaman basit ve kısa.
Sairam
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.