Java инвертированная карта - PullRequest
25 голосов
/ 22 августа 2011

Мне нужно создать обратную карту - выбрать уникальные значения и для них найти ключи.Кажется, что единственный способ состоит в том, чтобы перебрать все пары ключ / значение, потому что entrySet возвращает набор значений, не уникальных?Спасибо.

Ответы [ 8 ]

36 голосов
/ 22 августа 2011

Значения на карте могут быть не уникальными.Но если они (в вашем случае), вы можете сделать, как вы написали в своем вопросе, и создать общий метод для его преобразования:

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(.., ..) вернет "старое" значение для ключа.Если оно не равно нулю, вы можете выбросить new IllegalArgumentException("Map values must be unique") или что-то в этом роде.

9 голосов
/ 22 августа 2011

Взгляните на 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();
5 голосов
/ 13 мая 2015

Чтобы получить перевернутую форму данной карты в 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);
3 голосов
/ 22 августа 2011

Кажется, что единственный способ состоит в том, чтобы перебрать все пары ключ / значение, потому что 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);
2 голосов
/ 13 июля 2017

Я бы дал другой подход к этой проблеме, предоставив дополнительное измерение: повторяющиеся значения в 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]

1 голос
/ 17 февраля 2016

Apache Commons Collections также предоставляет интерфейс BidiMap для двунаправленных карт и несколько реализаций.

BidiMap JavaDoc

0 голосов
/ 17 августа 2018

С Гуавой

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

Вы получите взамен мультикарту (в основном карту списков).

0 голосов
/ 22 августа 2011

Вы должны предположить, что значения могут быть идентичны, так как контракт Map позволяет это.

На мой взгляд, лучшее решение заключается в использовании обертки. Он будет содержать исходное значение и добавить идентификатор. Его функция hashCode () будет полагаться на идентификатор, и вы предоставляете Getter для исходного значения. Код будет примерно таким:

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" и позволяет отображать все исходные ключи и значения.

...