Строка как ключ в hashmap

Я читал много сообщений за последний час, но я все еще не очень понимаю концепцию использования неизменяемых объектов в качестве ключей в Hashmap. У меня есть hashmap, который имеет свой ключ как String. Значение в hashmap - это MyStore, где MyStore представляет информацию о принадлежащих мне магазинах. Строка представляет адрес. В моем коде логика у меня есть, я сначала смотрю на карту для этого ключа, если присутствует → получаю его значение, если его нет в нем помещать в hashmap. Мой менеджер просто сказал мне, что ключ изменится в будущем, то есть адрес моих магазинов изменится в будущем. Он сказал в этом случае, моя логика первой проверки, если ключ существует, не будет работать. Я не понимаю, что он здесь подразумевает. Я хочу четко понимать нижеследующие моменты -

  • Разница между изменяемыми и неизменяемыми ключами для хэш-карты.
  • Что произойдет, если вы используете неизменяемый ключ, который может измениться? - Я знаю, что это не имеет смысла, но я хочу четко понять, о чем говорит мой менеджер.
  • Некоторые сообщения говорят о строках, если они используются в качестве ключей в кеше хэш-карты, их хэш-код. Что это значит?
  • Если можно сказать, что я использовал изменяемые объекты в качестве ключей в моем хэшмапе, который реализовал hashcode и equals, то будет ли он работать? Я предполагаю, что это произойдет, потому что если ключ изменится, метод contains будет выглядеть, если ключ присутствует. Если он отсутствует, он помещает запись, чтобы вы могли ее получить в будущем.

Я не хочу создавать дублирующийся пост, если это обсуждалось ранее. Если я пропустил чтение сообщения, на котором есть ответы на все мои вопросы, укажите мне. Если нет, пожалуйста, объясните в непрофессиональных терминах вышеуказанные вопросы, которые у меня есть, поэтому это полезно в будущем для других читателей:). Не стесняйтесь редактировать мою тему сообщения, поэтому в будущем, если у кого-то есть аналогичный вопрос, они приземляются здесь напрямую:)

Ответы

Ответ 1

Во-первых: как работает HashMap?

В основном у него есть массив, и когда вы кладете пару ключ-значение на карту, он сохраняется в одной из позиций в массиве. Позиция в массиве выбирается на основе результата ключа hashCode(), переданного методу хэширования. Почему это? Ну, если вы запрашиваете значение для определенного ключа, индекс в массиве, чтобы найти ключ и его связанное значение, можно просто пересчитать, чтобы снова найти индекс в массиве. (Еще одна логика необходима для работы с ключами, которые сопоставляются с одним и тем же индексом, но я просто пытаюсь понять вас основным механизмом). Затем equals() используется для проверки того, действительно ли ключ в вычисленном индексе запрошенный ключ.

  • Из этого должно быть немного более понятно, почему неизменяемые ключи лучше, чем изменяемые ключи. Постоянный ключ всегда будет поддерживать одно и то же значение hashCode(), а функция хеширования снова найдет правильный ведро (= индекс в массиве hashMap).

    Это не означает, что изменяемые ключи не могут работать. Переключаемый ключ будет работать, если мутации на ключе не влияют на хеш-код или если ключи просто не мутируются, пока используется hashMap.

  • Как можно изменить неизменный ключ? Ну, сам ключ, возможно, не сможет меняться, но отображение ключевого значения может измениться в бизнес-логике. Если вы создаете карту, используя адрес в качестве ключа, вы полагаетесь на то, что адрес магазина не изменится. Если адрес магазина изменяется, вы не найдете его на Карте, используя его новый адрес в качестве ключа. Ваш менеджер имеет действительную точку.

  • скорость поиска ключа на карте сильно зависит от скорости вычисления хэш-кода. Для String этот вычисление пересекает все символы в String. Если вы используете длинные строки в качестве ключей и имеете много доступа к карте, это может привести к созданию горлышка с производительностью. Таким образом, реализация Java String кэширует хеш-значение, поэтому оно будет вычисляться только один раз. Однако вы можете избежать вычисления хеш-кода, если снова использовать тот же экземпляр String (новые экземпляры не будут иметь кешированное значение). Вы можете использовать intern() ключи, которые вы используете, но считайте это только в том случае, если можно показать, что на самом деле есть шея бутылки производительности, так как String интернирование действительно имеет свои собственные служебные данные.

  • как описано в 1: изменяемые ключи могут работать, если их хэш-код не зависит от мутаций. например используя ключ "Клиент", где hashCode() основан только на имени клиента, то реализация клиента, которая только не позволяет изменять имя, но позволяет другим значениям изменять, является надежным ключом.

Ответ 2

  • Может возникнуть проблема, если вы измените свой изменчивый объект, используемый в качестве ключа. map.containsKey(modifiedKey) может возвращать false, даже если ключ есть, вам придется перебирать ключи, чтобы найти его. Поэтому старайтесь использовать неизменяемые или не изменять mutable, пока это ключ.

  • Постоянный объект никогда не меняется. Существуют методы, которые выглядят так, будто они меняют объект, но вместо этого создается новая копия. Пример:

    Строка a = "A";

    String b = a.substring(0);//подстрока создала копию "A" с не изменением вообще.

    a = a + b;//a + b создает новую строку "AA" без изменения предыдущих.

  • Это может помочь caching-hashes-in-java-collections, и это здорово why-are-immutable-objects-in-hashmaps-so-effective

  • Строка уже реализовала equals и hashcode, не нужно изобретать другой класс для использования вместо него, если вы не уверены, что вам это нужно.

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

Ответ 3

  • Неизменяемые ключи не могут измениться. Следовательно, хэш-код, который вычисляется во время вставки, не может измениться. Поэтому, когда вы пытаетесь получить элемент с карты, хэш-код объекта для получения вычисляется по известным хэш-кодам. Если ваш ключ изменился извне (он был изменен), новый хэш-код ключа будет отличаться от того, который вы вставляли.

  • Посмотрим пример. для (2 и 4)

    public class RandomPair {
        int p;
        int q;
    
        public RandomPair(int p, int q) {
            this.p = p;
            this.q = q;
        }
        @Override
        public int hashCode() {
            return 31 * p + q;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof RandomPair)) {
                return false;
            }
            if (obj == this) {
               return true;
            }
    
            RandomPair other = (RandomPair) obj;
            if (p != other.p)
                return false;
            if (q != other.q)
                return false;
            return true;
        }
    
        public static void main(String[] args) {
            RandomPair pair = new RandomPair(10, 10);
            Map<RandomPair, Integer> map = new HashMap<RandomPair, Integer>();
    
            map.put(pair, 1);
            System.out.println(map.get(pair)); //returns 1
    
            //someone somewhere just changed the value of pair
            pair.p = 20;
            //the object was the same, someone somewhere just changed value of pair and now you can't 
            //find it in the map
            System.out.println(map.get(pair));
    
            //had you made p and q final, this sort of modification wouldn't be possible
           //Strings are immutable and thus prevent this modification
        }
    }
    
  • Поскольку строки неизменяемы, значение хэш-кода после вычисления может быть снова использовано повторно. hashcode лениво вычисляется. т.е. при первом вызове hashcode, а затем кэшируется значение hashcode.

Ответ 4

В общем, ключи в хэшмапах должны быть неизменными.

См. this

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

Хэш вашего ключа вычисляется один раз во время вставки, hashmap сохраняет его, и он не будет автоматически обновляться после изменения вашего ключа. Вот почему существует предположение, что ключи будут неизменными.

Ваши варианты: 1. Не используйте изменяемые объекты в качестве ключей. Попробуйте найти другой ключ или используйте неизменную часть вашего прежнего ключевого объекта 2. Не изменяйте свои изменяемые объекты, пока они используются как клавиши

Ответ 5

  • Изменчивый ключ или объект означает, что вы можете изменить объект [путем изменения, я имею в виду, что вы можете изменять значения, представленные объектом]. Это повлияет на его хранение в HashMap, если логика, написанная на equals и hashcode, использует эти модифицируемые значения.

  • Неизменяемость в идеале означает, что после инициализации объект не может быть изменен. Но если мы говорим конкретно в терминах HashMap, то все переменные, которые используются внутри equals и hashcode, если они могут быть изменены, то этот объект не следует использовать в качестве ключа, иначе он может использоваться как ключ [но все же не рекомендуется ].

  • Его не только около String, любой о кеширует его hashcode. Hashcode генерируется снова и снова почти для всех объектов [есть причина, по которой я говорю почти так же, как в некоторых случаях она может измениться). Hashcode кэшируется в заголовке объекта.

  • Если вы хотите использовать изменяемый объект в качестве ключа, вы должны пойти на IdentityHashMap. Просто прочитайте о них, они могут быть полезны в таких случаях.