web-dev-qa-db-ja.com

Javaマップを反転

逆マップを作成する必要があります-一意の値を選択して、キーを見つけます。 entrySetは一意ではない値のセットを返すため、唯一の方法はすべてのキーと値のペアを反復することだと思われますか?ありがとう。

27
user710818

マップの値は一意ではない場合があります。しかし、それらが(あなたの場合)である場合は、質問に書いたとおりに行うことができ、それを変換する汎用メソッドを作成します。

_private static <V, K> Map<V, K> invert(Map<K, V> map) {

    Map<V, K> inv = new HashMap<V, K>();

    for (Entry<K, V> entry : map.entrySet())
        inv.put(entry.getValue(), entry.getKey());

    return inv;
}
_

Java 8:

_public static <V, K> Map<V, K> invert(Map<K, V> map) {
    return map.entrySet()
              .stream()
              .collect(Collectors.toMap(Entry::getValue, Entry::getKey));
}
_

使用例:

_public static void main(String[] args) {

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

    map.put("Hello", 0);
    map.put("World!", 1);

    Map<Integer, String> inv = invert(map);

    System.out.println(inv); // outputs something like "{0=Hello, 1=World!}"
}
_

補足:put(.., ..)メソッドは、キーの「古い」値を返します。 nullでない場合は、new IllegalArgumentException("Map values must be unique")またはそのようなものをスローできます。

37
dacwe

Google Guava BiMap をご覧ください。

使用例

Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");

Map<String, Integer> inverted = HashBiMap.create(map).inverse();
9
Rhangaun

Java 8で指定されたマップの反転形式を取得するには:

public static <K, V> Map<V, K> inverseMap(Map<K, V> sourceMap) {
    return sourceMap.entrySet().stream().collect(
        Collectors.toMap(Entry::getValue, Entry::getKey,
           (a, b) -> a) //if sourceMap has duplicate values, keep only first
        );
}

使用例

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

map.put(1, "one");
map.put(2, "two");

Map<String, Integer> inverted = inverseMap(map);
5
mikeyreilly

entrySetは一意ではない値のセットを返すため、すべてのキーと値のペアを反復する方法しかないと思われます

少なくとも片道です。次に例を示します。

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

map.put(1, "one");
map.put(2, "two");

Map<String, Integer> inverted = new HashMap<String, Integer>();

for (Integer i : map.keySet())
    inverted.put(map.get(i), i);

一意でない値の場合、このアルゴリズムは最後に見つかった値をそのキーにマップします。 (ほとんどのマップでは反復順序が定義されていないため、これは他のソリューションと同じくらい良いはずです。)

各キーで最初に見つかった値を保持したい場合は、次のように変更できます

if (!inverted.containsKey(map.get(i)))
    inverted.put(map.get(i), i);
3
aioobe

余分な次元を与えるこの問題への別のアプローチを与えます:EntrySetの重複する値

public static void main(String[] args) {

    HashMap<Integer, String> s = new HashMap<Integer, String>();
    s.put(1, "Value1");
    s.put(2, "Value2");
    s.put(3, "Value2");
    s.put(4, "Value1");

    /*
     * swap goes here
     */
    HashMap<String,List<Integer>> newMap = new HashMap<String, List<Integer>>();
    for (Map.Entry<Integer, String> en : s.entrySet()) {
        System.out.println(en.getKey() + " " + en.getValue());

        if(newMap.containsKey(en.getValue())){
            newMap.get(en.getValue()).add(en.getKey());
        } else {
            List<Integer> tmpList = new ArrayList<Integer>();
            tmpList.add(en.getKey());
            newMap.put(en.getValue(), tmpList);
        }
    }

    for(Map.Entry<String, List<Integer>> entry: newMap.entrySet()){
        System.out.println(entry.getKey() + " " + entry.getValue());
    }
}

T結果は次のようになります。

1値1
2値2
3値2
4値1
値1 [1、4]
値2 [2、3]

2
Oriol Biosca

Apache Commons Collectionは、いくつかの実装とともに、双方向マップ用の BidiMap インターフェースも提供します。

BidiMap JavaDoc

1

Mapコントラクトで許可されているため、値は同じであると想定する必要があります。

私の意見では、最良の解決策はラッパーを使用することです。元の値が含まれ、IDが追加されます。そのhashCode()関数はIDに依存し、元の値のゲッターを提供します。コードは次のようになります。

public class MapKey
{
    /**
     * A new ID to differentiate equal values 
     */
    private int _id;
    /**
     * The original value now used as key
     */
    private String _originalValue;

    public MapKey(String originalValue)
    {
        _originalValue = originalValue;
       //assuming some method for generating ids...
        _id = getNextId();
    }

    public String getOriginalValue()
    {
        return _originalValue;
    }

    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + _id;
        return result;
    }

    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        MapKey other = (MapKey) obj;
        if (_id != other._id)
            return false;
        return true;
    }

    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("MapKey value is ");
        sb.append(_originalValue);
        sb.append(" with ID number ");
        sb.append(_id);
        return sb.toString();
    }

マップを反転すると、次のようになります。

public Map <MapKey, Integer> invertMap(Map <Integer, String> map)
{

     Map <MapKey, Integer> invertedMap = new HashMap <MapKey, Integer>();

   Iterator<Entry<Integer, String>> it = map.entrySet().iterator();

   while(it.hasNext())
   {
       //getting the old values (to be reversed)
       Entry<Integer, String> entry = it.next();
       Integer oldKey = entry.getKey();
       String oldValue = entry.getValue();

       //creating the new MapKey
       MapKey newMapKey = new MapKey(oldValue);
       invertedMap.put(newMapKey, oldKey);
   }

   return invertedMap;
}

次のような値を出力します。

for(MapKey key : invertedMap.keySet())
       {
           System.out.println(key.toString() + " has a new value of " +  invertedMap.get(key));

       }

このコードはどれもテストされていませんが、「c」スタイルのチェックの代わりにOO継承の設計を使用し、すべての元のキーと値を表示できるため、これが最良の解決策だと思います。

0
toto220

グアバと

Multimaps.transformValues(Multimaps.index(map.entrySet(), Map.Entry::getValue),
        Map.Entry::getKey)

その代わりに、マルチマップ(基本的にはリストのマップ)を取得します。

0
Kaerber