重複するキーを持つマップ実装


116

重複するキーを持つマップが欲しいのですが。

私は多くのマップ実装があることを知っています(Eclipseは約50を示しています)ので、これを可能にするものがあるはずです。これを行う独自のマップを作成するのは簡単ですが、既存のソリューションを使用したいと思います。

たぶんcommons-collectionsやgoogle-collectionsに何かありますか?


4
これはどのように機能しますか?キーに関連付けられた値を要求し、このキーがマップに複数回存在する場合、どの値を返す必要がありますか?
Mnementh 2009年

getは例外をスローする可能性があります。このマップは反復のためにのみ必要です。
IAdapter 2009年

6
反復のためだけに必要な場合、なぜ最初にマップが必要なのですか?ペアのリストなどを使用してください
Tal Pressman

2
私のプログラム全体がすでにMapで動作しているので、データが重複したキーを持つ可能性があることがわかりました。別の方法でMapを使用することが非常に間違っている場合、Mapの実装は5つだけで、50以上はありません。
IAdapter 2009年

回答:


90

あなたはマルチマップを探しています、そして実際にcommons-collectionsとGuavaの両方はそのためのいくつかの実装を持っています。マルチマップでは、キーごとに値のコレクションを維持することで複数のキーを使用できます。つまり、単一のオブジェクトをマップに配置できますが、コレクションを取得します。

Java 5を使用できる場合Multimapは、ジェネリックに対応しているため、Gavavaを使用します。


3
また、このMultimapは、ApacheのようにMapであるように見せかけません。
Kevin Bourrillion、2009年

7
Googleのコレクションは、グアバに取って代わられていることに注意してくださいので、ここでマルチマップのグアバのバージョンへのリンクです:code.google.com/p/guava-libraries/wiki/...
ジョシュ・グローバー

ただし、Multimapは完全にシリアル化可能ではなく、非直列化されたインスタンスを役に立たなくする一時的なメンバーがあります
dschulten

@dschultenまあ、マルチマップはインターフェイスであり、どの実装を意味するのかを指定していません。com.google.common.collect.HashMultimapreadObject/ writeObject方法は、としてArrayListMultimapと不変{リスト、セット} Multimapはしません。役に立たないデシリアライズされたインスタンスは、報告する価値のあるバグだと思います。
nd。

1
Apacheコレクション4.0は、genericscommons.apache.org /proper/commons
collections/

35

Googleコレクションの外部ライブラリに依存する必要はありません。次のマップを実装するだけです。

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);
}

コードを微調整してください。


14
もちろん、Guavaのマルチマップに依存する必要はありません。それらを再実装したり、テストしたりする必要がないので、それはあなたの人生を楽にするだけです
PhiLho

これは、すべてのペアでシームレスな反復を許可しません。確かにもっと欠点があります。私は1つの追加クラスも必要とする私のソリューションを提案しようとしていましたが、@ Mnementhの答えはそれだけであることがわかりました。
マーク

2
基本的なコードを書くことは常に賢いというわけではありません。グーグルはより良いテストを持っている可能性が高い
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));

出力は次のとおりです。

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

注:ライブラリファイルをインポートする必要があります。

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

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

またはhttps://commons.apache.org/proper/commons-collections/download_collections.cgi

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

2
私のプロジェクトではSpringを使用しているため、ドキュメントhttp://docs.spring.io/spring-framework/docs/current/javadoc-api/org/に
ajup '19年

18

通常のHashMapの値の値の配列を渡すだけで、重複するキーをシミュレートでき、使用するデータを決定するのはあなた次第です。

また、私はMultiMapだけを使用することもできますが、私自身はキーを複製するという考えは好きではありません。


ありがとうございました!使用しTreeMap<String, ArrayList<MyClass>>て私の重複するキーのニーズを解決しました。
ジョー

10

(コメントで書いたように)キーと値のペアのリストを反復処理する場合は、リストまたは配列の方が適しています。まず、キーと値を組み合わせます。

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

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

}

Class1とClass2を、キーと値に使用するタイプに置き換えます。

これで、それらを配列またはリストに入れて、それらを反復処理できます。

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

add()またはput()をどのように実装しますか?次元をハードコアしたくない。
アミットクマールグプタ

2
この場合、リストを使用します。2番目のサンプルは、List <Pair>ペア= new List <Pair>();に変わります。forループは同じままです。次のコマンドでペアを追加できます。pairs.add(pair);
Mnementh

これはおそらく正直に言うと最良の答えです。
PaulBGD 2014

6

この問題は、マップエントリのリストで解決できますList<Map.Entry<K,V>>。外部ライブラリやMapの新しい実装を使用する必要はありません。マップエントリは次のように作成できます。 Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);



3

私の過ちから学んでください...自分でこれを実装しないでください。グアバのマルチマップは進むべき道です。

マルチマップで必要な一般的な拡張機能は、重複するキーと値のペアを許可しないことです。

あなたの実装でこれを実装/変更するのは面倒です。

グアバでは次のようにシンプルです:

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

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

2

この問題のわずかに異なるバリアントがありました。2つの異なる値を同じキーに関連付ける必要がありました。他の人を助けるためにここに投稿するだけで、値としてHashMapを導入しました。

/* @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 );
        }
}

上記のコードでは、キーframeIDが各行の入力ファイルの最初の文字列から読み取られ、frameTypeHashの値は残りの行を分割することによって構築され、ファイルが複数の行(同じフレームIDキーに関連付けられた異なる値で)、frameTypeHashは値として最後の行で上書きされました。Stringオブジェクトを値フィールドとして別のHashMapオブジェクトに置き換えました。これにより、単一のキーから異なる値へのマッピングを維持することができました。


2

派手なライブラリは必要ありません。マップは一意のキーによって定義されるため、曲げないでください。リストを使用してください。ストリームは強力です。

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"),
);

以上です。使用例:

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;
        }
    }

@daspilkerありがとうございます。現在、編集内容のみを表示しています。それが編集されている場合、誰かが私のスニペットがふさわしいと思うのを見るといいですね。
Gabrial David 2017

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

この冗長なソリューションには複数の欠点があり、エラーが発生しやすくなります。これは、すべての値のコレクションをインスタンス化し、値を追加または削除する前にその存在を確認し、値が残っていない場合は手動で削除する必要があることなどを意味します。

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

java-map-duplicate-keys


1

そのようなMultiMap実装はどうですか?

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

重複するキーでマップを実装しようとしているコンテキストについても説明できますか?より良い解決策があると私は確信しています。マップは、正当な理由で一意のキーを保持することを目的としています。本当にやりたいのなら 常にクラスを拡張して、衝突軽減機能があり、同じキーを持つ複数のエントリを保持できるようにする単純なカスタムマップクラスを作成できます。

注:衝突軽減キーを実装して、衝突するキーを「常に」一意のセットに変換する必要があります。オブジェクトのハッシュコードなどをキーに追加するような単純なものですか?


1
コンテキストは、キーは一意になると考えていましたが、実際はそうではない可能性があることがわかりました。頻繁に使用されることはないので、すべてをリファクタリングしたくありません。
IAdapter 2009年

小さなXMLファイルをデータ型のようなハッシュマップに変換したい。問題はXMLファイルの構造が固定されていないことだけです
Amit Kumar Gupta

0

完全にするために、Apache Commons CollectionsにはMultiMapもあります。もちろん欠点は、Apache CommonsがGenericsを使用しないことです。


1
それらのMultiMapはMapを実装しますが、Mapメソッドのコントラクトを壊すことに注意してください。それは私を困らせます。
Kevin Bourrillion、2009年

0

ビットハックを使用すると、重複キーでHashSetを使用できます。警告:これはHashSet実装に大きく依存します。

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

重複するキーがある場合、キーは複数の値に対応する場合があります。明白な解決策は、キーをこれらの値のリストにマップすることです。

たとえばPythonでは:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.