Как использование HashMap равно () для ArrayList & lt; Integer & gt ;? [Дубликат]

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

См. « Исключение NullReferenceException при проверке пользовательского AuthorizationAttribute » для несколько подробного примера.

44
задан donnyton 20 October 2011 в 21:48
поделиться

7 ответов

Многие уважаемые разработчики, такие как Брайан Гетц и Джош Блох, отметили, что:

Если значение hashCode () объекта может меняться в зависимости от его состояния, тогда мы должны быть осторожны при использовании таких объектов, как ключи в коллекциях на основе хэшей, чтобы гарантировать, что мы не позволяем их состоянию изменять, когда они используются в качестве хеш-ключей. Все хэш-коллекции предполагают, что хэш-значение объекта не изменяется, пока оно используется в качестве ключа в коллекции. Если хэш-код ключа должен был измениться, пока он был в коллекции, некоторые непредсказуемые и запутанные последствия могли бы последовать. Обычно это не проблема на практике - распространенной практикой является использование изменяемого объекта, такого как List, как ключа в HashMap.

59
ответ дан aleroot 24 August 2018 в 04:50
поделиться

Если хэш-код ключа изменяется после того, как пара ключей и значений хранится в HashMap, карта не сможет получить запись.

Хэш-код ключа может измениться, если ключевой объект изменен , Mutable keys в HahsMap может привести к потере данных.

3
ответ дан Andrew Barber 24 August 2018 в 04:50
поделиться

Как объясняют другие, это опасно.

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

Другим трюком будет использование адреса, например. (intptr_t) reinterpret_cast<void*>(this) в качестве основы для хэша.

Во всех случаях вам нужно отказаться от хэширования изменяющегося состояния объекта.

2
ответ дан Basile Starynkevitch 24 August 2018 в 04:50
поделиться

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

T keyOne = ...;
T keyTwo = ...;

// At this point keyOne and keyTwo are different instances and 
// keyOne.equals(keyTwo) is true.

HashMap myMap = new HashMap();

myMap.push(keyOne, "Hello");

String s1 = (String) myMap.get(keyOne); // s1 is "Hello"
String s2 = (String) myMap.get(keyTwo); // s2 is "Hello" 
                                        // because keyOne equals keyTwo

mutate(keyOne);

s1 = myMap.get(keyOne); // returns "Hello"
s2 = myMap.get(keyTwo); // not found

Вышеуказанное верно, если ключ хранится как ссылка. Обычно в Java это так. Например, в .NET, если ключ является типом значения (всегда передается по значению), результат будет другим:

T keyOne = ...;
T keyTwo = ...;

// At this point keyOne and keyTwo are different instances 
// and keyOne.equals(keyTwo) is true.

Dictionary myMap = new Dictionary();

myMap.Add(keyOne, "Hello");

String s1 = (String) myMap[keyOne]; // s1 is "Hello"
String s2 = (String) myMap[keyTwo]; // s2 is "Hello"
                                    // because keyOne equals keyTwo

mutate(keyOne);

s1 = myMap[keyOne]; // not found
s2 = myMap[keyTwo]; // returns "Hello"

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

5
ответ дан Ivaylo Slavov 24 August 2018 в 04:50
поделиться

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

5
ответ дан onit 24 August 2018 в 04:50
поделиться

Это небезопасно или желательно. Значение, отображаемое клавишей 1, никогда не может быть восстановлено. При выполнении поиска большинство хэш-карт будут делать что-то вроде

Object get(Object key) {
    int hash = key.hashCode();
    //simplified, ignores hash collisions,
    Entry entry = getEntry(hash);
    if(entry != null && entry.getKey().equals(key)) {
        return entry.getValue();
    }
    return null;
}

. В этом примере key1.hashcode () теперь указывает на неправильное ведро хеш-таблицы, и вы не сможете получить value1 с ключом.

Если вы сделали что-то вроде

Key key1 = new Key(0, 0);
map.put(key1, value1);
key1.setA(5);
Key key2 = new Key(0, 0);
map.get(key2);

Это также не будет извлекать значение1, так как key1 и key2 уже не равны, поэтому эта проверка

    if(entry != null && entry.getKey().equals(key)) 

не удастся.

17
ответ дан sbridges 24 August 2018 в 04:50
поделиться

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

Давайте посмотрим здесь пример:

public class MapKeyShouldntBeMutable {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub
    Map<Employee,Integer> map=new HashMap<Employee,Integer>();

    Employee e=new Employee();
    Employee e1=new Employee();
    Employee e2=new Employee();
    Employee e3=new Employee();
    Employee e4=new Employee();
    e.setName("one");
    e1.setName("one");
    e2.setName("three");
    e3.setName("four");
    e4.setName("five");
    map.put(e, 24);
    map.put(e1, 25);
    map.put(e2, 26);
    map.put(e3, 27);
    map.put(e4, 28);
    e2.setName("one");
    System.out.println(" is e equals e1 "+e.equals(e1));
    System.out.println(map);
    for(Employee s:map.keySet())
    {
        System.out.println("key : "+s.getName()+":value : "+map.get(s));
    }
}

  }
 class Employee{
String name;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

@Override
public boolean equals(Object o){
    Employee e=(Employee)o;
    if(this.name.equalsIgnoreCase(e.getName()))
            {
        return true;
            }
    return false;

}

public int hashCode() {
    int sum=0;
    if(this.name!=null)
    {
    for(int i=0;i<this.name.toCharArray().length;i++)
    {
        sum=sum+(int)this.name.toCharArray()[i];
    }
    /*System.out.println("name :"+this.name+" code : "+sum);*/
    }
    return sum;

}

}

Здесь мы пытаемся добавить к карте изменчивый объект «Сотрудник». Он будет работать хорошо, если все добавленные ключи различны. Если у меня есть переопределенные равные и хэш-коды для класса сотрудников.

См. Сначала Я добавил «e», а затем «e1». Для обоих из них equals () будет истинным, а hashcode будет таким же. Таким образом, карта видит, что добавляется тот же ключ, поэтому он должен заменить старое значение значением e1. Затем мы добавили e2, e3, e4, на данный момент мы прекрасны.

Но когда мы меняем значение уже добавленного ключа i.e «e2» как единое целое, оно становится ключом, подобным ранее добавленному. Теперь карта будет вести себя проводной. В идеале e2 должен заменить существующий такой же ключ i.e e1. Но теперь карта также берет это. И вы получите это в o / p:

 is e equals e1 true
{Employee@1aa=28, Employee@1bc=27, Employee@142=25, Employee@142=26}
key : five:value : 28
key : four:value : 27
key : one:value : 25
key : one:value : 25

См. Здесь оба ключа с одним и тем же значением. Итак, его неожиданность. Теперь снова запустите ту же программу, изменив здесь e2.setName("diffnt");, который является e2.setName("one"); здесь ... Теперь o / p будет следующим:

 is e equals e1 true
{Employee@1aa=28, Employee@1bc=27, Employee@142=25, Employee@27b=26}
key : five:value : 28
key : four:value : 27
key : one:value : 25
key : diffnt:value : null

Итак, добавив изменение изменчивого ключа на карте не рекомендуется.

0
ответ дан smruti ranjan 24 August 2018 в 04:50
поделиться
Другие вопросы по тегам:

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