Java: разные двойные и двойные в сравнении
Я знаю, что Double
является классом-оболочкой, и он обертывает номер Double
. Сегодня я видел еще одно главное отличие:
double a = 1.0;
double b = 1.0;
Double c = 1.0;
Double d = 1.0;
System.out.println(a == b); // true
System.out.println(c == d); // false
Так странно со мной!!!
Итак, если мы используем Double
, каждый раз мы должны сделать что-то вроде этого:
private static final double delta = 0.0001;
System.out.println(Math.abs(c-d) < delta);
Я не могу объяснить, почему Double делают прямое сравнение неправильным. Пожалуйста, объясните мне.
Спасибо:)
Ответы
Ответ 1
c
и d
являются технически двумя разными объектами, а оператор ==
сравнивает только ссылки.
c.equals(d)
лучше, поскольку он сравнивает значения, а не ссылки. Но все же не идеальный. Сравнение значений с плавающей запятой непосредственно должно учитывать некоторую ошибку (epsilon) (Math.abs(c - d) < epsilon
).
Обратите внимание, что:
Integer c = 1;
Integer d = 1;
здесь сравнение даст true
, но это более сложное (Integer
внутреннее кэширование, описанное в JavaDoc Integer.valueOf()
)
Этот метод всегда будет кэшировать значения в диапазоне от -128 до 127 включительно и может кэшировать другие значения за пределами этого диапазона.
Почему valueOf()
? Поскольку этот метод неявно используется для реализации автобоксинга:
Integer c = Integer.valueOf(1);
Integer d = Integer.valueOf(1);
См. также
Ответ 2
При применении к выражениям типа класса ==
всегда будет выполнять сравнительное сравнение (JLS раздел 15.21.3). Итак, эта строка:
System.out.println(c == d);
проверяет, относятся ли теги c
и d
к тем же объектам. Авто-бокс в Java всегда (я считаю) создает новый объект для float
и double
(ситуация сложнее для целых типов 1). Поэтому c
и d
относятся к разным объектам, поэтому он печатает false
.
Если вы хотите сравнить объекты для равенства, вам нужно явно вызвать equals
:
System.out.println(c.equals(d));
С double
вместо этого используется числовое равенство - как указано в разделе 15.21.1. Отсюда разница в поведении.
1 Для интегрального автобоксинга "маленькие" значения кэшируются - поэтому autoboxing 5 (скажем) будет возвращать одну и ту же ссылку каждый раз. Определение "маленький" является специфичным для реализации, но оно гарантировано в диапазоне от -128 до 127. См. Нижнюю часть раздел 5.1.7 для подробности.
Ответ 3
Используйте equals()
для проверки равенства двух объектов. ==
проверяет, ссылаются ли 2 ссылки на один и тот же объект в памяти.
Ответ 4
Проверка содержимого является надежной только при ==
при проверке примитивных типов. Для типов объектов всегда лучше использовать метод equals
:
c.equals(d)