В чем разница между мелким и глубоким равенством? Как это применяется к кешированию? - PullRequest
6 голосов
/ 18 апреля 2011

В моих заметках обнаружил следующее, но я не могу понять это:

Классы-обертки примитивного типа реализуют кэширование для ограниченного числа значений.
Это гарантирует, что ограниченноеколичество глубоко равных объектов-обёрток также незначительно равно: если o1.equals( o2 ), то o1 == o2.
Например, new Integer( 0 ) == new Integer( 0 ).
В общем случае это не всегда работает.
Например, new Integer (666) == new Integer (666)
может не сохраняться.
Причина кэширования заключается в том, что он экономит память.
В общем случае кэширование работает для «малых» примитивных значений.

Я не понимаю, что подразумевается под этим, или в чем разница между глубоким (.equals ()) и поверхностным (==) равными.На практике я знаю, что .equals должны использоваться для объектов и == для интегральных значений, но фактическое обоснование этого намекает на меня.

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

Ответы [ 5 ]

6 голосов
/ 18 апреля 2011

Ну, на самом деле неглубокий / глубокий анализ отличается от == / равный анализ:

  1. == сравнивает для идентификации объекта, то есть вы проверяете, являются ли операнды одинаковыми на самом деледве ссылки на одну и ту же область памяти), тогда как equals сравнивает эквивалентность объектов, то есть «логическое» значение двух, возможно, не идентичных объектов, одинаково.Если для двух объектов

    a == b
    

    то верно, что

    a.equals(b) // if a != null
    

    , но не во всех случаях верно обратное.

  2. мелкое / глубокое различиеимеет смысл только для equals сравнения.Мелкий означает, что вы сравниваете только непосредственное содержимое двух объектов, чтобы определить, являются ли они «равными» в вашем смысле, тогда как глубокий означает, что вы сравниваете содержимое своих объектов рекурсивно, пока все, что вам нужно для сравнения, - это примитивные поля.Если вы определяете equals метод ваших объектов как последовательность вызовов equals в полях экземпляров этих объектов, вы используете глубокое сравнение.Если вы определяете equals с помощью оператора == для сравнения составных типов, таких как строки, то вы используете поверхностное сравнение - и это неправильно в Java.

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

6 голосов
/ 18 апреля 2011

Когда вы делаете ==, вы сравниваете ссылки на равенство.Это означает, что вы говорите: «Адрес в памяти одинаков для обоих объектов?»

Когда вы делаете .equals(), вы сравниваете сами объекты на равенство.Это означает, что вы говорите «эти два объекта считают себя равными?»

Приведенный пример был плохим. Единственное кэширование, выполняемое для этих чисел, предписанное JLS, - это метод .valueOf(). Конструктор не кэшируется.

Кроме того, JLS указывает только минимум вы должны кэшировать [-128: 127].Реализации JVM могут кэшировать больше, если они того пожелают.Это означает, что Integer.valueOf(500) == Integer.valueOf(500) может быть false на некоторых машинах, но true на других.

class biziclop {

    public static void main(String[] args) {
        System.out.println(new Integer(5) == new Integer(5));
        System.out.println(new Integer(500) == new Integer(500));

        System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
        System.out.println(Integer.valueOf(500) == Integer.valueOf(500));
    }
}

Результат:

C:\Documents and Settings\glow\My Documents>java biziclop
false
false
true
false

C:\Documents and Settings\glow\My Documents>

Более подробный ответ здесь (комментарии - это жемчужина!): Почему люди все еще используют примитивные типы в Java?

2 голосов
/ 18 апреля 2011

Сначала: new Integer(0) == new Integer(0) будет никогда оценивается как true, так как new всегда создает новый объект, обходя любой механизм автобокс-кэширования, который может существовать.1008 *

То, о чем вы, вероятно, слышали, это автобокс (т.е. автоматическое преобразование примитивных значений в их соответствующие классы-обертки при необходимости).Автобокс использует механизм, который также доступен с помощью методов-классов valueOf().Другими словами: автобокс int к Integer работает почти так же, как вызов Integer.valueOf(int).

Integer.valueOf(0) == Integer.valueOf(0) будет оценивать true, потому чтообщие значения (то есть значения с низким абсолютным значением) кэшируются.Вы получите тот же самый Integer объект, когда вы вызовете valueOf(0) два раза подряд.Это не обязательно верно для более высоких значений (например, 666 в вашем примере).

1 голос
/ 18 апреля 2011

equals () проверяет, являются ли два объекта по существу одинаковыми, но может возвращать true для двух различных объектов;т.е. два разных скрепки "равны".«==» для ссылочных типов проверяет, ссылаются ли две ссылки на один и тот же объект, т. е. скрепка == только для себя.== тесты идентичность , что equals тестирование эквивалентность .

Вы можете иметь два различных объекта типа Integer с 0 в них (они equals());кэширование означает сохранение объектов и их повторное использование, когда это возможно.

0 голосов
/ 18 апреля 2011

То, что вы называете «мелким равным», это идентичность : две ссылки (т. Е. Объекты) идентичны, если они являются одинаковыми экземплярами.Если вы знаете, что такое указатели на других языках, вы можете сравнить идентичность с равенством указателей.

То, что вы называете "глубоким равным", равно равенство : два объекта a и b являютсяравно, если a.equals(b) возвращает true (и, надеюсь, наоборот).Корректность равенства сильно зависит от способа реализации метода equals.См. Javadoc класса Object для более подробной информации.

...