Yinelenen anahtarlarla harita uygulaması


116

Yinelenen anahtarlara sahip bir haritam olsun istiyorum.

Birçok harita uygulaması olduğunu biliyorum (Eclipse bana yaklaşık 50 gösteriyor), bu yüzden buna izin veren bir tane olması gerektiğine bahse girerim. Bunu yapan kendi haritanızı yazmanın kolay olduğunu biliyorum, ancak mevcut bazı çözümleri kullanmayı tercih ederim.

Belki ortak koleksiyonlarda veya google koleksiyonlarında bir şey?


4
Bu nasıl çalışmalı? Bir anahtarla ilişkilendirilmiş bir değer sorarsanız ve bu anahtar haritada birden çok kez bulunuyorsa, hangi değer döndürülmelidir?
Mnementh

get istisna atabilir, bu haritaya yalnızca yineleme için ihtiyacım var.
IAdapter

6
Sadece yineleme için ihtiyacınız varsa, neden ilk etapta bir haritaya ihtiyacınız var? Bir çift listesi veya başka bir şey kullanın ...
Tal Pressman

2
Çünkü tüm programım zaten Map ile çalışıyor ve şimdi verilerin yinelenen anahtarlara sahip olmasının mümkün olduğunu keşfettim. Haritayı farklı bir şekilde kullanmak çok yanlışsa, 50'den fazla değil, yalnızca 5 Harita uygulamasına sahip oluruz.
IAdapter

Yanıtlar:


90

Bir çoklu harita arıyorsunuz ve aslında hem ortak koleksiyonların hem de Guava'nın bunun için çeşitli uygulamaları var. Çoklu haritalar, anahtar başına bir değer koleksiyonunu koruyarak birden çok anahtara izin verir, yani haritaya tek bir nesne koyabilirsiniz, ancak bir koleksiyonu geri alabilirsiniz.

Java 5 kullanabiliyorsanız Multimap, jeneriklere duyarlı olduğu için Guava'yı tercih ederim .


3
Ayrıca, bu Çoklu Harita, apache'nin yaptığı gibi bir Harita gibi görünmüyor.
Kevin Bourrillion

7
Google Koleksiyonlarının yerini Guava aldığını unutmayın, bu yüzden MultiMap'in Guava sürümünün bağlantısı: code.google.com/p/guava-libraries/wiki/…
Josh Glover

Bununla birlikte, Multimap tam olarak serileştirilemez, serileştirilmiş bir örneği işe yaramaz hale getiren geçici üyelere sahiptir
dschulten

@dschulten Pekala, Multimap bir arayüz ve hangi uygulamayı kastettiğinizi belirtmiyorsunuz. ArrayListMultimap ve Immutable {List, Set} Multimap gibi / yöntemleri com.google.common.collect.HashMultimapvardır . Seri durumdan çıkarılmış işe yaramaz bir örneği rapor etmeye değer bir hata olarak düşünürdüm. readObjectwriteObject
nd.


35

Google Koleksiyonları harici kitaplığına bağımlı olmamıza gerek yok. Aşağıdaki Haritayı kolayca uygulayabilirsiniz:

Map<String, ArrayList<String>> hashMap = new HashMap<String, ArrayList>();

public static void main(String... arg) {
   // Add data with duplicate keys
   addValues("A", "a1");
   addValues("A", "a2");
   addValues("B", "b");
   // View data.
   Iterator it = hashMap.keySet().iterator();
   ArrayList tempList = null;

   while (it.hasNext()) {
      String key = it.next().toString();             
      tempList = hashMap.get(key);
      if (tempList != null) {
         for (String value: tempList) {
            System.out.println("Key : "+key+ " , Value : "+value);
         }
      }
   }
}

private void addValues(String key, String value) {
   ArrayList tempList = null;
   if (hashMap.containsKey(key)) {
      tempList = hashMap.get(key);
      if(tempList == null)
         tempList = new ArrayList();
      tempList.add(value);  
   } else {
      tempList = new ArrayList();
      tempList.add(value);               
   }
   hashMap.put(key,tempList);
}

Lütfen kodda ince ayar yaptığınızdan emin olun.


14
Elbette Guava'nın Multimap'ına güvenmenize gerek yok. Onları yeniden uygulamanıza, test
etmenize

Bu, tüm çiftlerde sorunsuz yinelemeye izin vermez. Elbette daha fazla eksiklik var. Ekstra bir ders gerektiren çözümümü önermek üzereydim, sonra @ Mnementh'in cevabının tam da bu olduğunu gördüm.
Mark Jeronimus

2
temel kod yazmak her zaman o kadar akıllıca değildir. Google'ın daha iyi testlere sahip olma olasılığı daha yüksek
senseiwu

26
Multimap<Integer, String> multimap = ArrayListMultimap.create();

multimap.put(1, "A");
multimap.put(1, "B");
multimap.put(1, "C");
multimap.put(1, "A");

multimap.put(2, "A");
multimap.put(2, "B");
multimap.put(2, "C");

multimap.put(3, "A");

System.out.println(multimap.get(1));
System.out.println(multimap.get(2));       
System.out.println(multimap.get(3));

Çıktı:

[A,B,C,A]
[A,B,C]
[A]

Not: kütüphane dosyalarını içe aktarmamız gerekiyor.

http://www.java2s.com/Code/Jar/g/Downloadgooglecollectionsjar.htm

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

veya https://commons.apache.org/proper/commons-collections/download_collections.cgi

import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;

2
İyi bir öneri, projemde Spring'i kullandığım için, http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/
ajup

18

Normal bir HashMap'teki değer için bir dizi değer iletebilir, böylece yinelenen anahtarları simüle edebilirsiniz ve hangi verilerin kullanılacağına karar vermek size kalmıştır.

Ayrıca bir MultiMap de kullanabilirsiniz , ancak kendim de anahtarların kopyalanması fikrinden hoşlanmıyorum.


Teşekkür ederim! Kullanmak TreeMap<String, ArrayList<MyClass>>yinelenen anahtar ihtiyaçlarımı çözdü.
Joe

10

Bir anahtar-değer çiftleri listesi hakkında yineleme istiyorsanız (açıklamada yazdığınız gibi), bir Liste veya bir dizi daha iyi olmalıdır. Önce anahtarlarınızı ve değerlerinizi birleştirin:

public class Pair
{
   public Class1 key;
   public Class2 value;

   public Pair(Class1 key, Class2 value)
   {
      this.key = key;
      this.value = value;
   }

}

Class1 ve Class2'yi anahtarlar ve değerler için kullanmak istediğiniz türlerle değiştirin.

Şimdi onları bir diziye veya listeye koyabilir ve üzerinde yineleyebilirsiniz:

Pair[] pairs = new Pair[10];
...
for (Pair pair : pairs)
{
   ...
}

Add () veya put () 'u nasıl uygularım? Bir dizi boyutu zorlamak istemiyorum.
Amit Kumar Gupta

2
Bu durumda bir Liste kullanın. İkinci örnek, List <Pair> çiftleri = new List <Pair> () olarak değişir; For-döngü aynı kalır. Bu komutla bir çift ekleyebilirsiniz: pairs.add (çift);
Mnementh

Dürüst olmak gerekirse muhtemelen en iyi cevap budur.
PaulBGD

6

Bu sorun bir harita girişi listesi ile çözülebilir List<Map.Entry<K,V>>. Ne harici kütüphaneleri ne de Map'in yeni uygulamasını kullanmamıza gerek yok. Aşağıdaki gibi bir harita girişi oluşturulabilir: Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);



3

Hatalarımdan ders alın ... lütfen bunu kendi başınıza uygulamayın. Guava multimap gitmenin yoludur.

Çoklu haritalarda gerekli olan yaygın bir geliştirme, yinelenen anahtar-değer çiftlerine izin vermemek.

Bunu bir uygulamanızda uygulamak / değiştirmek can sıkıcı olabilir.

Guava'da bu kadar basit:

HashMultimap<String, Integer> no_dupe_key_plus_val = HashMultimap.create();

ArrayListMultimap<String, Integer> allow_dupe_key_plus_val = ArrayListMultimap.create();

2

Bu sorunun biraz farklı bir varyantı vardı: İki farklı değeri aynı anahtarla ilişkilendirmek gerekiyordu. Başkalarına yardımcı olması durumunda buraya gönderiyorum, değer olarak bir HashMap ekledim:

/* @param frameTypeHash: Key -> Integer (frameID), Value -> HashMap (innerMap)
   @param innerMap: Key -> String (extIP), Value -> String
   If the key exists, retrieve the stored HashMap innerMap 
   and put the constructed key, value pair
*/
  if (frameTypeHash.containsKey(frameID)){
            //Key exists, add the key/value to innerHashMap
            HashMap innerMap = (HashMap)frameTypeHash.get(frameID);
            innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);

        } else {
            HashMap<String, String> innerMap = new HashMap<String, String>();
            innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
            // This means the key doesn't exists, adding it for the first time
            frameTypeHash.put(frameID, innerMap );
        }
}

Yukarıdaki kodda, ana çerçeve kimliği, her satırdaki bir girdi dosyasının ilk dizesinden okunur, frameTypeHash değeri, kalan satırı bölerek oluşturulur ve orijinal olarak String nesnesi olarak depolanır, bir süre boyunca dosyanın birden çok satıra sahip olmaya başladığı ( farklı değerlerle) aynı frameID anahtarıyla ilişkilendirildiğinden, değer olarak son satırı frameTypeHash üzerine yazılmıştır. String nesnesini, değer alanı olarak başka bir HashMap nesnesiyle değiştirdim, bu, farklı değer eşlemelerine tek anahtarın korunmasına yardımcı oldu.


2

Süslü kitaplara gerek yok. Haritalar benzersiz bir anahtarla tanımlanır, bu yüzden onları bükmeyin, bir liste kullanın. Akışlar güçlüdür.

import java.util.AbstractMap.SimpleImmutableEntry;

List<SimpleImmutableEntry<String, String>> nameToLocationMap = Arrays.asList(
    new SimpleImmutableEntry<>("A", "A1"),
    new SimpleImmutableEntry<>("A", "A2"),
    new SimpleImmutableEntry<>("B", "B1"),
    new SimpleImmutableEntry<>("B", "B1"),
);

Ve bu kadar. Kullanım örnekleri:

List<String> allBsLocations = nameToLocationMap.stream()
        .filter(x -> x.getKey().equals("B"))
        .map(x -> x.getValue())
        .collect(Collectors.toList());

nameToLocationMap.stream().forEach(x -> 
do stuff with: x.getKey()...x.getValue()...

1
class  DuplicateMap<K, V> 
{
    enum MapType
    {
        Hash,LinkedHash
    }

    int HashCode = 0;
    Map<Key<K>,V> map = null;

    DuplicateMap()
    {
        map = new HashMap<Key<K>,V>();
    }

    DuplicateMap( MapType maptype )
    {
        if ( maptype == MapType.Hash ) {
            map = new HashMap<Key<K>,V>();
        }
        else if ( maptype == MapType.LinkedHash ) {
            map = new LinkedHashMap<Key<K>,V>();
        }
        else
            map = new HashMap<Key<K>,V>();
    }

    V put( K key, V value  )
    {

        return map.put( new Key<K>( key , HashCode++ ), value );
    }

    void putAll( Map<K, V> map1 )
    {
        Map<Key<K>,V> map2 = new LinkedHashMap<Key<K>,V>();

        for ( Entry<K, V> entry : map1.entrySet() ) {
            map2.put( new Key<K>( entry.getKey() , HashCode++ ), entry.getValue());
        }
        map.putAll(map2);
    }

    Set<Entry<K, V>> entrySet()
    {
        Set<Entry<K, V>> entry = new LinkedHashSet<Map.Entry<K,V>>();
        for ( final Entry<Key<K>, V> entry1 : map.entrySet() ) {
            entry.add( new Entry<K, V>(){
                private K Key = entry1.getKey().Key();
                private V Value = entry1.getValue();

                @Override
                public K getKey() {
                    return Key;
                }

                @Override
                public V getValue() {
                    return Value;
                }

                @Override
                public V setValue(V value) {
                    return null;
                }});
        }

        return entry;
    }

    @Override
    public String toString() {
        StringBuilder builder = new  StringBuilder();
        builder.append("{");
        boolean FirstIteration = true;
        for ( Entry<K, V> entry : entrySet() ) {
            builder.append( ( (FirstIteration)? "" : "," ) + ((entry.getKey()==null) ? null :entry.getKey().toString() ) + "=" + ((entry.getValue()==null) ? null :entry.getValue().toString() )  );
            FirstIteration = false;
        }
        builder.append("}");
        return builder.toString();
    }

    class Key<K1>
    {
        K1 Key;
        int HashCode;

        public Key(K1 key, int hashCode) {
            super();
            Key = key;
            HashCode = hashCode;
        }

        public K1 Key() {
            return Key;
        }

        @Override
        public String toString() {
            return  Key.toString() ;
        }

        @Override
        public int hashCode() {

            return HashCode;
        }
    }

Teşekkürler @daspilker. Düzenlemenizi yalnızca şimdi görüyorum. Birinin düzenlenmişse pasajımın değerli olduğunu görmesi çok güzel.
Gabrial David

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

bu ayrıntılı çözümün birçok dezavantajı vardır ve hatalara açıktır. Her değer için bir Koleksiyon başlatmamız, bir değer eklemeden veya kaldırmadan önce varlığını kontrol etmemiz, hiçbir değer kalmadığında manuel olarak silmemiz, vb.

2, org.apache.commons.collections4.MultiMap interface
3, com.google.common.collect.Multimap interface 

java-haritası-yinelenen-tuşları


1

peki ya böyle bir MultiMap uygulaması?

public class MultiMap<K, V> extends HashMap<K, Set<V>> {
  private static final long serialVersionUID = 1L;
  private Map<K, Set<V>> innerMap = new HashMap<>();

  public Set<V> put(K key, V value) {
    Set<V> valuesOld = this.innerMap.get(key);
    HashSet<V> valuesNewTotal = new HashSet<>();
    if (valuesOld != null) {
      valuesNewTotal.addAll(valuesOld);
    }
    valuesNewTotal.add(value);
    this.innerMap.put(key, valuesNewTotal);
    return valuesOld;
  }

  public void putAll(K key, Set<V> values) {
    for (V value : values) {
      put(key, value);
    }
  }

  @Override
  public Set<V> put(K key, Set<V> value) {
    Set<V> valuesOld = this.innerMap.get(key);
    putAll(key, value);
    return valuesOld;
  }

  @Override
  public void putAll(Map<? extends K, ? extends Set<V>> mapOfValues) {
    for (Map.Entry<? extends K, ? extends Set<V>> valueEntry : mapOfValues.entrySet()) {
      K key = valueEntry.getKey();
      Set<V> value = valueEntry.getValue();
      putAll(key, value);
    }
  }

  @Override
  public Set<V> putIfAbsent(K key, Set<V> value) {
    Set<V> valueOld = this.innerMap.get(key);
    if (valueOld == null) {
      putAll(key, value);
    }
    return valueOld;
  }

  @Override
  public Set<V> get(Object key) {
    return this.innerMap.get(key);
  }

  @Override
  etc. etc. override all public methods size(), clear() .....

}

0

Yinelenen anahtarlara sahip bir harita uygulamaya çalıştığınız bağlamı da açıklayabilir misiniz? Eminim daha iyi bir çözüm olabilir. Haritalar, iyi bir nedenden ötürü benzersiz anahtarları tutmaya yöneliktir. Ama gerçekten yapmak isteseydin; Sınıfı her zaman, bir çarpışma azaltma işlevi olan ve aynı anahtarlarla birden çok girişi tutmanıza olanak tanıyan basit bir özel harita sınıfı yazarak genişletebilirsiniz.

Not: Çakışan anahtarlar "her zaman" benzersiz kümeye dönüştürülecek şekilde çarpışma azaltma işlevi uygulamanız gerekir. Nesne karma kodu ile anahtar eklemek gibi basit bir şey mi?


1
Bağlam, anahtarların benzersiz olacağını düşünmüştüm, ancak olmayabileceği ortaya çıktı. Sık kullanılmayacağı için her şeyi yeniden düzenlemek istemiyorum.
IAdapter

küçük bir XML dosyasını veri türü gibi hashmap'e dönüştürmek istiyorum. Tek sorun, XML dosyasının yapısının düzeltilmemiş olması
Amit Kumar Gupta

0

tam olarak, Apache Commons Collections ayrıca bir MultiMap'e sahiptir . Elbette dezavantajı, Apache Commons'ın Generics kullanmamasıdır.


1
MultiMap'lerinin Map uyguladığını, ancak Map yöntemlerinin sözleşmelerini bozduğunu unutmayın. Bu beni rahatsız ediyor.
Kevin Bourrillion

0

Biraz hack ile HashSet'i yinelenen anahtarlarla kullanabilirsiniz. UYARI: bu büyük ölçüde HashSet uygulamasına bağlıdır.

class MultiKeyPair {
    Object key;
    Object value;

    public MultiKeyPair(Object key, Object value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public int hashCode() {
        return key.hashCode();
    }
}

class MultiKeyList extends MultiKeyPair {
    ArrayList<MultiKeyPair> list = new ArrayList<MultiKeyPair>();

    public MultiKeyList(Object key) {
        super(key, null);
    }

    @Override
    public boolean equals(Object obj) {
        list.add((MultiKeyPair) obj);
        return false;
    }
}

public static void main(String[] args) {
    HashSet<MultiKeyPair> set = new HashSet<MultiKeyPair>();
    set.add(new MultiKeyPair("A","a1"));
    set.add(new MultiKeyPair("A","a2"));
    set.add(new MultiKeyPair("B","b1"));
    set.add(new MultiKeyPair("A","a3"));

    MultiKeyList o = new MultiKeyList("A");
    set.contains(o);

    for (MultiKeyPair pair : o.list) {
        System.out.println(pair.value);
    }
}

0

Yinelenen anahtarlar varsa, bir anahtar birden fazla değere karşılık gelebilir. Bariz çözüm, anahtarı bu değerlerin bir listesiyle eşleştirmektir.

Örneğin Python'da:

map = dict()
map["driver"] = list()
map["driver"].append("john")
map["driver"].append("mike")
print map["driver"]          # It shows john and mike
print map["driver"][0]       # It shows john
print map["driver"][1]       # It shows mike

0

Bunu kullandım:

java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();

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.