Есть ли структура данных, которая работает как двухсторонняя хэшмап? [Дубликат]

У меня была одна и та же проблема с выполнением заданий над кластером Mesos в режиме кластера.

Чтобы использовать драйвер JDBC, необходимо добавить зависимость к пути к системному классу не к пути класса framework. Я нашел способ сделать это, добавив зависимость в файл spark-defaults.conf в каждом экземпляре кластера.

Добавляемые свойства: spark.driver.extraClassPath и spark.executor.extraClassPath, а путь должен находиться в локальной файловой системе.

91
задан Jonik 5 November 2009 в 19:23
поделиться

7 ответов

В Java API такого класса нет. Класс Apache Commons, который вы хотите, будет одной из реализаций BidiMap .

Будучи математиком, я бы назвал такую ​​структуру биекцией.

99
ответ дан dARKpRINCE 25 August 2018 в 17:09
поделиться

В дополнение к Apache Commons Guava также имеет BiMap .

71
ответ дан ColinD 25 August 2018 в 17:09
поделиться
  • 1
    Спасибо за информацию! Я придерживаюсь apache на данный момент (если нет веских причин не делать этого?) – Kip 3 November 2009 в 22:10
  • 2
    Я не могу предложить хорошее сравнение с коллекциями apache, но в коллекциях google есть много приятных вещей, которые, я думаю, заставили бы его задуматься. – ColinD 3 November 2009 в 22:25
  • 3
    Одно из преимуществ Google Collections заключается в том, что у него есть дженерики, тогда как Commons Collections - нет. – Mark 3 November 2009 в 22:38
  • 4
    Для сравнения двух библиотек см. Цитаты в этом ответе: stackoverflow.com/questions/787446/… (и исходное интервью). Это очевидно по отношению к Google, по очевидным причинам, но, тем не менее, я думаю, что можно с уверенностью сказать, что теперь лучше с Google Коллекциями. – Jonik 5 November 2009 в 19:21
  • 5
    Ссылка BiMap не работает. Используйте этот . – Mahsa Mohammadkhani 20 August 2016 в 17:48

Вдохновленный ответом GETah Я решил написать что-то подобное себе с некоторыми улучшениями:

  • Класс реализует Map<K,V> -Interface
  • Двунаправленность действительно гарантируется, если позаботиться об этом при изменении значения на put (по крайней мере, я надеюсь это гарантировать)

Использование подобно обычной карте , чтобы получить обратный вид на картографический вызов getReverseView(). Содержимое не копируется, возвращается только представление.

Я не уверен, что это абсолютно безупречно (на самом деле это, вероятно, нет), поэтому не стесняйтесь комментировать, если вы заметили какие-то недостатки и я 'll обновить ответ.

public class BidirectionalMap<Key, Value> implements Map<Key, Value> {

    private final Map<Key, Value> map;
    private final Map<Value, Key> revMap;

    public BidirectionalMap() {
        this(16, 0.75f);
    }

    public BidirectionalMap(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

    public BidirectionalMap(int initialCapacity, float loadFactor) {
        this.map = new HashMap<>(initialCapacity, loadFactor);
        this.revMap = new HashMap<>(initialCapacity, loadFactor);
    }

    private BidirectionalMap(Map<Key, Value> map, Map<Value, Key> reverseMap) {
        this.map = map;
        this.revMap = reverseMap;
    }

    @Override
    public void clear() {
        map.clear();
        revMap.clear();
    }

    @Override
    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return revMap.containsKey(value);
    }

    @Override
    public Set<java.util.Map.Entry<Key, Value>> entrySet() {
        return Collections.unmodifiableSet(map.entrySet());
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public Set<Key> keySet() {
        return Collections.unmodifiableSet(map.keySet());
    }

    @Override
    public void putAll(Map<? extends Key, ? extends Value> m) {
        m.entrySet().forEach(e -> put(e.getKey(), e.getValue()));
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public Collection<Value> values() {
        return Collections.unmodifiableCollection(map.values());
    }

    @Override
    public Value get(Object key) {
        return map.get(key);
    }

    @Override
    public Value put(Key key, Value value) {
        Value v = remove(key);
        getReverseView().remove(value);
        map.put(key, value);
        revMap.put(value, key);
        return v;
    }

    public Map<Value, Key> getReverseView() {
        return new BidirectionalMap<>(revMap, map);
    }

    @Override
    public Value remove(Object key) {
        if (containsKey(key)) {
            Value v = map.remove(key);
            revMap.remove(v);
            return v;
        } else {
            return null;
        }
    }

}
0
ответ дан Community 25 August 2018 в 17:09
поделиться

Здесь мои 2 цента.

Или вы можете использовать простой метод с дженериками. Кусок торта.

public static <K,V> Map<V, K> invertMap(Map<K, V> toInvert) {
    Map<V, K> result = new HashMap<V, K>();
    for(K k: toInvert.keySet()){
        result.put(toInvert.get(k), k);
    }
    return result;
}

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

3
ответ дан Fulvius 25 August 2018 в 17:09
поделиться

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

    public class BidirectionalMap<KeyType, ValueType>{
        private Map<KeyType, ValueType> keyToValueMap = new ConcurrentHashMap<KeyType, ValueType>();
        private Map<ValueType, KeyType> valueToKeyMap = new ConcurrentHashMap<ValueType, KeyType>();

        synchronized public void put(KeyType key, ValueType value){
            keyToValueMap.put(key, value);
            valueToKeyMap.put(value, key);
        }

        synchronized public ValueType removeByKey(KeyType key){
            ValueType removedValue = keyToValueMap.remove(key);
            valueToKeyMap.remove(removedValue);
            return removedValue;
        }

        synchronized public KeyType removeByValue(ValueType value){
            KeyType removedKey = valueToKeyMap.remove(value);
            keyToValueMap.remove(removedKey);
            return removedKey;
        }

        public boolean containsKey(KeyType key){
            return keyToValueMap.containsKey(key);
        }

        public boolean containsValue(ValueType value){
            return keyToValueMap.containsValue(value);
        }

        public KeyType getKey(ValueType value){
            return valueToKeyMap.get(value);
        }

        public ValueType get(KeyType key){
            return keyToValueMap.get(key);
        }
    }
19
ответ дан GETah 25 August 2018 в 17:09
поделиться
  • 1
    Вы значительно улучшите производительность containsValue (), изменив его, чтобы вернуть valueToKeyMap.containsKey (значение) – JT. 23 April 2013 в 01:46
  • 2
    Я бы не использовал эту карту так, как сейчас, потому что двунаправленность ломается, если ключ (или значение) повторно добавляется с другим значением (или ключом), что будет действительным использованием для обновления ключевого IMO. – Qw3ry 7 February 2017 в 14:31

Если никаких столкновений не происходит, вы всегда можете добавить оба направления к одному и тому же HashMap: -)

10
ответ дан rsp 25 August 2018 в 17:09
поделиться
  • 1
    если бы не улыбка, я бы спустил тебя за это .... :) – Kip 3 November 2009 в 22:12
  • 2
    @Kip: Почему? В некоторых контекстах это вполне законное решение. Так было бы иметь два хэш-карты. – Lawrence Dol 4 November 2009 в 00:38
  • 3
    нет, это уродливый, хрупкий хак. он требует поддержания двунаправленного свойства на каждом get () и put (), и его можно передать другим методам, которые изменяют карту, даже не зная о двунаправленном свойстве. возможно, это будет нормально как локальная переменная внутри метода, который не передается нигде, или если он был немодифицирован сразу после создания. но даже тогда он хрупкий (кто-то приходит и настраивает эту функцию и ломает двунаправленную направленность таким образом, что не всегда будет сразу проявлять себя как проблема) – Kip 4 November 2009 в 02:22
  • 4
    @Kip, я согласен с тем, что такое использование должно храниться внутри класса с использованием этой карты, но ваше последнее замечание выполняется только в том случае, если соответствующие тесты JUnit неаккуратные :-) – rsp 4 November 2009 в 10:32
  • 5
    полностью взломать. – b3bop 20 January 2012 в 11:04

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

Я тоже искал двунаправленную HashMap, иногда это является самым простым из наиболее полезных ответов.

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

SomeType[] keys1 = new SomeType[NUM_PAIRS];
OtherType[] keys2 = new OtherType[NUM_PAIRS];

Как только вы знаете индекс 1 из двух ключей, вы можете легко запросить другой. Таким образом, ваши методы поиска выглядят примерно так:

SomeType getKey1(OtherType ot);
SomeType getKey1ByIndex(int key2Idx);
OtherType getKey2(SomeType st); 
OtherType getKey2ByIndex(int key2Idx);

Предполагается, что вы используете подходящие объектно-ориентированные структуры, где только методы модифицируют эти массивы / ArrayLists, было бы очень просто поддерживать их параллель. Еще проще для ArrayList, так как вам не придется перестраивать, если размер массивов изменяется, если вы добавляете / удаляете в тандеме.

0
ответ дан ThatOneGuy 25 August 2018 в 17:09
поделиться
  • 1
    Вы теряете важную функцию HashMaps, а именно O (1) поиск. Подобная реализация потребует сканирования через один из массивов, пока вы не найдете индекс элемента, который вы ищете, который представляет собой O (n) – Kip 1 February 2015 в 21:29
  • 2
    Да, это очень верно и является одним довольно большим недостатком. Однако в моей личной ситуации я действительно занимался необходимостью трехстороннего ключевого листинга, и я всегда знал бы хотя бы 1 из ключей раньше времени, поэтому для меня лично это не проблема. Спасибо, что указали, что, хотя, похоже, я пропустил этот важный факт в своем оригинальном посте. – ThatOneGuy 3 February 2015 в 16:48
Другие вопросы по тегам:

Похожие вопросы: