Карта уникальных значений Java

Мне нужна карта с уникальными ключами, а также сохранение повторяющихся значений только один раз. Интерфейс будет картой, но я не хочу, чтобы одно и то же значение использовало память несколько раз. Например:

В обычной реализации Map, такой как HashMap, предполагается, что value.equals(value') и value.equals(value''), но value!=value' и value!=value' и value!=value'', если мы:

put(key1, value);
put(key2, value');
put(key3, value'');

Затем значение будет сохранено три раза.

Я попытался сделать свою собственную реализацию, которая выглядит так:

class MyMap2<K, V> extends HashMap<K, V> {
    private Map<V, V> values;

    public MyMap2() {
        values = new HashMap<V, V>();
    }

    @Override
    public V put(final K key, final V value) {
        V v = values.get(value);
        if (v == null) {
            v = value;
            values.put(v, v);
        }
        return super.put(key, v);
    }
}

Эта реализация сохраняет значение только один раз (обратите внимание, что я использую одно и то же значение). Но есть ли какая-нибудь Map, которая уже реализует такую ​​структуру данных с помощью get/put O(1)?

Обратите внимание, что BiMap бесполезен, так как это вызовет ошибку в случае дублирования значений.


person user3698770    schedule 14.05.2015    source источник
comment
Я не уверен, что это должно достичь. Вы не храните копии объекта, вы храните несколько ссылок на него. И ваша реализация, похоже, просто проходит через суперкласс .put() в конце?   -  person FatalError    schedule 14.05.2015
comment
Зачем вам это нужно? Когда вы ответите, почему, вы можете понять, что это на самом деле не нужно...   -  person Germann Arlington    schedule 14.05.2015
comment
Как насчет случая, когда два уникальных ключа будут иметь одинаковое значение. Например, Джону и Биллу по 20 лет?   -  person MaxZoom    schedule 14.05.2015
comment
На самом деле вы не используете много памяти, когда помещаете одни и те же значения на карту. Вы не копируете объекты. Так что не стесняйтесь использовать только HashMap   -  person lopushen    schedule 14.05.2015
comment
Я не верю, что этот вопрос задает то же самое, что и вопрос, помеченный как дубликат. Но это только мое мнение.   -  person FatalError    schedule 14.05.2015
comment
Мне нужно использовать такую ​​реализацию, например, в случае, если у меня есть два одинаковых значения: code code   -  person user3698770    schedule 14.05.2015
comment
Эта реализация сохраняет значение только один раз — это неправильно. Я запустил этот код с mymap.put("hello","world") и mymap.put("hi","world"), и обе пары ключ-значение были сохранены. Ваш код не делает то, что вы хотите.   -  person ktorn    schedule 14.05.2015
comment
Пожалуйста, проверьте мое редактирование   -  person user3698770    schedule 14.05.2015
comment
Хорошо, этот вопрос действительно нуждается в переформулировке. Если вы правы, у вас есть проблема с хранением объектов с разными ссылками (разными объектами), но имеющих одинаковое значение (как во внутренних переменных-членах). Это правильно?   -  person ktorn    schedule 14.05.2015
comment
Да, ты прав!   -  person user3698770    schedule 14.05.2015
comment
Хорошо, теперь я тебя понимаю. Но ваш код по-прежнему этого не делает. Я все еще вижу несколько ссылок на объекты, которые равны (я проверял на равенство, прежде чем помещать оба).   -  person ktorn    schedule 14.05.2015
comment
Ссылки для меня не проблема, проблема в размере хранимых объектов.   -  person user3698770    schedule 14.05.2015
comment
Если ссылки не совпадают, вы храните несколько копий объектов. Я только что перепроверил, values.get(value) всегда возвращает null, даже если значение уже должно быть на карте. Даже после переопределения equals в тестируемом классе. Это на самом деле странно.   -  person ktorn    schedule 14.05.2015
comment
Если вы протестируете следующий код, он войдет в условие v == null всего два раза: ` map.put(key1,value1); map.put (ключ1, значение1); map.put (ключ2, значение1); map.put (ключ2, значение2); `   -  person user3698770    schedule 14.05.2015
comment
Не используйте String в качестве значений для своих тестов. Создайте класс Person и выполните new Person("name"). Затем создайте person1, person2, person3 с тем же именем. Затем добавьте их все на свою карту с разными ключами. Эти 3 человека могут иметь одно и то же имя (вы даже можете переопределить equals, чтобы гарантировать, что person1.equals(person2) но ваша карта будет хранить разные объекты (в 3 раза больше памяти), а не несколько ссылок на один и тот же объект (в 1 раз больше памяти).   -  person ktorn    schedule 14.05.2015
comment
Если вы переопределите метод equals, он будет работать и для вас, метод hashcode   -  person user3698770    schedule 14.05.2015
comment
Ага. Вам нужно переопределить как equals, так и hashCode, тогда это сработает.   -  person ktorn    schedule 14.05.2015
comment
Любая лучшая реализация?   -  person user3698770    schedule 14.05.2015
comment
Не то чтобы я знаю. Тот факт, что вам нужно переопределить эти 2 метода, чтобы он работал, означает, что это не то, что вы найдете в обычной библиотеке.   -  person ktorn    schedule 14.05.2015


Ответы (2)


Эта реализация уже обещает постоянное время операций получения/ввода. Худший случай — при вставке нового значения, которое еще никогда не было видно. В этом случае вы:

  1. Попытайтесь найти значение на карте values — O(1), так как это HashMap.
  2. Не найти его и поставить значение в карту values - O(1), так как это HashMap.
  3. Поместите пару ключ-значение в super - O(1), так как это HashMap.

Вы найдете лучший способ реализации этой логики, но не на порядок.

РЕДАКТИРОВАТЬ:
Обратите внимание, что реализация может put в super дважды, что просто избыточно. Его можно настроить, чтобы он был немного чище:

@Override
public V put(final K key, final V value) {
    V v = values.get(value);
    if (v == null) {
        v = value
        values.put(v, v);
    } 
    return super.put(key, v);
}
person Mureinik    schedule 14.05.2015
comment
Спасибо. Я согласен с вами насчет super.put(), но есть ли другая возможная реализация, которая не использует Map‹V,V› (например, Map‹V›, чтобы не хранить два раза одно и то же значение? - person user3698770; 14.05.2015
comment
В случае put(v,v) вы сохраняете только ссылку на v (а не 2 копии), поэтому, хотя это и не элегантно, но не неэффективно с точки зрения памяти. - person ktorn; 14.05.2015
comment
Итак, мой вопрос: есть ли более элегантный способ сделать это? - person user3698770; 14.05.2015

Об этом уже спрашивали раньше.

Ознакомьтесь с BiMap.

person ktorn    schedule 14.05.2015
comment
Пример OP с размещением одного и того же объекта для 3 ключей вызовет исключение IllegalArgumentException с использованием BiMap. - person FatalError; 14.05.2015
comment
Как сказал FatalError, в этом случае BiMap бесполезен. - person user3698770; 14.05.2015
comment
В вопросе вы не указали желаемое поведение при сохранении одного и того же значения. Код мне тоже непонятен. На первый взгляд кажется, что он все еще хранит повторяющиеся значения. super.put(key, value) делает то же самое, что и super.put(key, v), начиная с value == v. - person ktorn; 14.05.2015
comment
Обратите внимание, что значение "равно значению", но не является одним и тем же объектом, поэтому оно будет сохранено в памяти несколько раз. - person user3698770; 14.05.2015