Почему Long.valueOf(0).equals(Integer.valueOf(0)) является ложным?

StackOverflow https://stackoverflow.com/questions/445990

  •  22-07-2019
  •  | 
  •  

Вопрос

Эти вопросы вызваны странное поведение HashMap.put()

Думаю, я понимаю, почему Map<K,V>.put берет K но Map<K,V>.get принимает Object, похоже, что невыполнение этого требования приведет к поломке слишком большого количества существующего кода.

Теперь мы попадаем в очень подверженный ошибкам сценарий:

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L,"Five"); // compiler barfs on m.put(5, "Five")
m.contains(5); // no complains from compiler, but returns false

Нельзя ли было решить эту проблему, вернув true, если Long значение было в пределах int диапазон и значения равны?

Это было полезно?

Решение

Вот источник Long.java

public boolean equals(Object obj) {
    if (obj instanceof Long) {
        return value == ((Long)obj).longValue();
    }
    return false;
}

Т.е.чтобы быть равным, он должен быть типом Long.Я думаю, что ключевое различие между:

long l = 42L
int i = 42;
l == i

и ваш пример выше заключается в том, что с примитивами может произойти неявное расширение значения int, однако для типов объектов нет правил для неявного преобразования из Integer в Long.

Также проверьте Java-головоломки, там есть много подобных примеров.

Другие советы

Вообще говоря, хотя это не является строго выраженным в контракте для equals () , объекты не должны считаться равными другим объектам, не принадлежащим к тому же классу (даже если это подкласс). Рассмотрим симметричное свойство - если a.equals (b) имеет значение true, то b.equals (a) также должно быть истинным.

У нас есть два объекта: foo класса Super и bar класса Sub, который расширяется equals(). Теперь рассмотрим реализацию foo.equals(bar) в Super, особенно когда он называется Object. Foo знает только, что bar строго типизируется как HashSet<Sub>, поэтому для точного сравнения необходимо проверить, что это экземпляр Super, а если нет, вернуть false. Да, так что эта часть в порядке. Теперь он сравнивает все поля экземпляра и т. Д. (Или независимо от фактической реализации сравнения) и находит их равными. Пока все хорошо.

Однако по контракту он может вернуть true только если он знает, что bar.equals (foo) также вернет true. Поскольку bar может быть любым подклассом Super, неясно, будет ли переопределен метод equals () (и, возможно, будет). Таким образом, чтобы убедиться, что ваша реализация верна, вам нужно написать ее симметрично и убедиться, что два объекта одного класса.

В сущности, объекты разных классов нельзя считать равными - в этом случае, например, в <=> можно вставить только один из них.

Да, но все сводится к алгоритму сравнения и тому, как далеко нужно выполнить преобразования. Например, что вы хотите, чтобы произошло, когда вы пытаетесь m.Contains("5")? Или если вы передадите ему массив с 5 в качестве первого элемента? Проще говоря, похоже, что он подключен &, Если типы разные, ключи разные & Quot;.

Затем возьмите коллекцию с ключом object. Что вы хотите, чтобы произошло, если вы put a 5L, а затем попытаетесь получить 5, "5", ...? Что если вы 5F а int, а long и short хотите проверить на float?

Поскольку это универсальная коллекция (или шаблонная, или как вы хотите ее называть), она должна будет проверить и выполнить какое-то специальное сравнение для определенных типов значений. Если K равен double, проверьте, является ли переданный объект <=>, <=>, <=>, <=>, ..., затем выполните преобразование и сравните. Если K равен <=>, проверьте, является ли переданный объект ...

Вы поняли.

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

Ваш вопрос кажется разумным, но было бы нарушением общих правил для equals (), если не его контракт, возвращать true для двух разных типов.

Часть дизайна языка Java заключалась в том, что Objects никогда неявно конвертировал в другие типы, в отличие от C ++. Это было частью создания Java маленьким простым языком. Разумная часть сложности C ++ связана с неявными преобразованиями и их взаимодействием с другими функциями.

Кроме того, Java имеет четкую и видимую дихотомию между примитивами и объектами. Это отличается от других языков, где эта разница скрыта под прикрытием в качестве оптимизации. Это означает, что вы не можете ожидать, что Long и Integer будут вести себя как long и int.

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

Таким образом, ваш код должен быть ....

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L, "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(5L)); // true

Вы забыли, что java автоматически блокирует ваш код, поэтому приведенный выше код будет эквивалентен

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(new Long(5L), "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(new Long(5))); // true
System.out.println(m.containsKey(new Long(5L))); // true

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

Другие ответы адекватно объясняют, почему это терпит неудачу, но ни один из них не описывает, как написать код, который менее подвержен ошибкам вокруг этой проблемы. Необходимо помнить о необходимости добавления приведений типов (без помощи компилятора), суффиксов примитивов с L и т. Д., Только IMHO.

Я настоятельно рекомендую использовать библиотеку коллекций GNU, если у вас есть примитивы (и во многих других случаях). Например, есть TLongLongHashMap, который хранит вещи как единое целое. В результате вы никогда не будете сталкиваться с боксом / распаковкой и никогда не будете сталкиваться с неожиданным поведением:

TLongLongHashMap map = new TLongLongHashMap();
map.put(1L, 45L);
map.containsKey(1); // returns true, 1 gets promoted to long from int by compiler
int x = map.get(1); // Helpful compiler error. x is not a long
int x = (int)map.get(1); // OK. cast reassures compiler that you know
long x = map.get(1); // Better.

и так далее. Нет необходимости понимать правильный тип, и компилятор выдаст вам ошибку (которую вы можете исправить или переопределить), если вы делаете что-то глупое (попытайтесь сохранить long в int).

Правила автоматического приведения означают, что сравнения также работают правильно:

if(map.get(1) == 45) // 1 promoted to long, 45 promoted to long...all is well

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top