Подтвердить что ты не робот

Карта инвертирования Java

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

4b9b3361

Ответ 1

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

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") или что-то в этом роде.

Ответ 2

Взгляните на 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();

Ответ 3

Чтобы получить инвертированную форму заданного отображения в 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);

Ответ 4

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

В случае не уникальных значений этот алгоритм отобразит последнее найденное в нем значение. (Так как порядок итераций undefined для большинства карт, это должно быть так же хорошо, как любое решение.)

Если вы действительно хотите сохранить первое значение для каждого ключа, вы можете изменить его на

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

Ответ 5

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

Ответ 6

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

BidiMap JavaDoc

Ответ 7

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

По-моему, лучшим решением является использование обертки. Он будет содержать исходное значение и добавить идентификатор. Его функция 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));

       }

Ни один из этих кодов не проверен, но я считаю это лучшим решением, поскольку он использует дизайн наследования OO вместо проверки стиля "c" и позволяет отображать все оригинальные ключи и значения.

Ответ 8

С Гуавой

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

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