Как использование оператора == повышает производительность по сравнению с оператором equals?

В «Эффективной JAVA» Джошуа Блоха, когда я читал о статических фабричных методах, было следующее утверждение.

Способность статических фабричных методов возвращать один и тот же объект при повторных вызовах позволяет классам поддерживать строгий контроль над тем, какие экземпляры существуют в любое время. Классы, которые делают это, называются управляемыми экземплярами. Есть несколько причин для написания классов, управляемых экземпляром. Управление экземпляром позволяет классу гарантировать, что он является одноэлементным (пункт 3) или не содержит экземпляров (пункт 4). Кроме того, он позволяет неизменяемому классу (статья 15) гарантировать, что не существует двух одинаковых экземпляров: a.equals(b) тогда и только тогда, когда a==b. Если класс предоставляет такую ​​гарантию, то его клиенты могут использовать оператор == вместо метода equals(Object), что может повысить производительность. Типы Enum (статья 30) обеспечивают эту гарантию.

Чтобы выяснить, как оператор == повышает производительность, я посмотрел String.java

Я видел этот фрагмент

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

Что он имеет в виду под улучшением производительности? как это приносит улучшение производительности.

Он хочет сказать следующее

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

Прав ли я в этом выводе?

Если я ошибаюсь, можете ли вы помочь мне понять это?


person Harish Kayarohanam    schedule 17.11.2013    source источник
comment
Но значит ли это, что String a = xyz; и String b =xyz вместе являются пустой тратой памяти и невозможны? Это ли не вариант использования?   -  person Harish Kayarohanam    schedule 17.11.2013
comment
Строковые литералы всегда интернированы в Java. Итак, в вашем примере вы обнаружите, что a == b.   -  person Chris Jester-Young    schedule 17.11.2013


Ответы (6)


Если каждый класс может гарантировать, что a.equals(b) тогда и только тогда, когда a==b , это означает, что он вводит косвенное требование о том, что не может быть объектов, ссылающихся на 2 разных пространства памяти, и при этом хранить одни и те же данные, то есть память. растрата . Если они содержат одни и те же данные, они являются одним и тем же объектом. То есть они указывают на одно и то же место в памяти.

Да, именно на это и претендует автор.

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

Это работает, например, для enums.

И даже если бы кто-то вызвал метод equals (что было бы хорошей практикой защитного программирования, вы не хотите привыкать использовать == для объектов ИМХО), этот метод можно было бы реализовать как простой == (вместо того, чтобы искать при потенциально сложном состоянии объекта).

Между прочим, даже для «обычных» методов equals (таких как String) в их реализации, вероятно, будет хорошей идеей сначала проверить идентичность объекта, а затем быстро просмотреть состояние объекта (что и делает String#equals, когда вы узнали).

person Thilo    schedule 17.11.2013
comment
+1 за предоставление enum в качестве примера того, для чего это действительно работает. - person Jason C; 17.11.2013
comment
Я категорически не согласен с частью, касающейся cannot work for mutable classes. На самом деле, это equals то, что перестает работать всякий раз, когда вы помещаете изменяемые объекты в коллекцию. Ссылочное равенство работает всегда, просто у него другая семантика. - person maaartinus; 17.11.2013
comment
@maaartinus: мне трудно понять, как интернирование будет работать с изменяемыми объектами. И equals не может иметь семантику, отличную от равенства ссылок в этом сценарии (в том, что они становятся взаимозаменяемыми, заключается весь смысл этого упражнения). - person Thilo; 17.11.2013
comment
@Thilo: Представьте себе класс Person с уникальным неизменяемым id. Предположим, что два человека равны, если у них одинаковые id (такие предположения распространены при работе с базами данных). Такой Person можно было использовать в качестве ключа на картах и ​​интернировать. Изменение других атрибутов будет работать. - person maaartinus; 17.11.2013
comment
@maaartinus Ю. Я бы никогда не сделал метод equals, который сравнивал бы только id, если есть другие изменяемые поля, потому что они являются частью наблюдаемого состояния объекта. - person Chris Jester-Young; 17.11.2013
comment
@Chris Jester-Young: Но тогда ваши карты и наборы, содержащие эти объекты, ломаются каждый раз, когда вы что-то меняете. Класс Person с equals на основе идентификатора представляет личность человека, в то время как класс, проверяющий все свойства, представляет собой своего рода имя, возраст и любой другой контейнер. У обоих есть свое применение (хотя я бы предпочел первое). - person maaartinus; 17.11.2013
comment
@maaartinus Это зависит от того, считаете ли вы свой тип типом значения или ссылочным типом. Я чувствую, что объекты доступа к данным являются типами значений. В том, как мы разрабатываем наш код, мы используем Integer для ключей карты, а не самих объектов доступа к данным. У нас есть отдельный кеш (обычно со значениями с мягкой ссылкой) для поиска этих объектов, если их повторная выборка требует больших затрат. - person Chris Jester-Young; 17.11.2013
comment
Ваша концепция использования только id при сохранении изменчивости всего остального напоминает мне об использовании значений перечисления с отслеживанием состояния. Лично я предпочитаю сохранять значения перечисления без сохранения состояния, особенно если вызывающий код напрямую использует значения перечисления. В ограниченных особых случаях, например, если я реализую синглтон на основе перечисления, с сохранением состояния все в порядке; перечисление - это просто деталь реализации. Все дело в уменьшении количества сюрпризов для пользователей вашего кода. - person Chris Jester-Young; 17.11.2013
comment
@Thilo: я думаю, что в сценарии, на который намекают, никогда не может существовать два разных экземпляра Person с одним и тем же идентификатором. Если одна часть кода вызывает CreatePerson("123-45-6789), а ни одна другая часть кода никогда этого не делала, он создаст новый объект со свойствами по умолчанию. Если другой фрагмент кода вызовет CreatePerson("123-45-6789");, он получит ссылку на тот же изменяемый объект. По сути, идентификатор человека и ссылка на человека становятся эквивалентными. - person supercat; 18.11.2013
comment
@maaartinus: Я правильно вас понимаю [см. мой комментарий выше]? - person supercat; 18.11.2013
comment
@supercat: Наверное, да. При использовании Hibernate вы можете столкнуться с некоторыми проблемами с equals, и одно из решений основано только на идентификаторе. Это приносит другие проблемы, и интернирование объектов, как вы написали, кажется полезным. - person maaartinus; 18.11.2013
comment
@maaartinus: мне не совсем понятно, где метод equals, основанный только на идентификаторе, был бы полезен или уместен. Если объекты изменяемы, и идентификатор объекта должен быть его идентификатором, то никогда не должно существовать двух разных объектов с одним и тем же идентификатором, поэтому переопределение equals на основе идентификатора никогда не должно вести себя иначе, чем тест эталонного равенства по умолчанию. Что мне не хватает? - person supercat; 18.11.2013
comment
@supercat: Как вы можете добиться того, чтобы отдельные объекты никогда не существовали? Ваша CreatePerson фабрика может это сделать, но как насчет объектов, поступающих из базы данных, десериализации или чего-то еще? Вы можете захотеть интернировать их немедленно, но после определения equals вам не нужно этого делать. - person maaartinus; 18.11.2013
comment
@maaartinus: не должно быть никаких средств для создания объекта, кроме как через фабрики, которые будут отслеживать все существующие экземпляры. Это нужно не только для того, чтобы убедиться, что equals работает. Если mailroom.morningWorker и payroll.afternoonWorker являются разными объектами Person с идентификатором #12345, что произойдет, если этот человек вложит сверхурочные по 10 долларов в обоих местах, поэтому код выполняет и mailroom.morningWorker.currentWeekPay += 1000;, и payroll.afternoonWorker.currentWeekPay += 1000? Наличие двух разных объектов, каждый со своим собственным currentWeekPay, — прямой путь к катастрофе. - person supercat; 18.11.2013
comment
@maaartinus: можно было бы, чтобы объект Person не инкапсулировал ничего, кроме неизменной комбинации идентификатора человека и идентификатора базы данных, и в этом случае наличие повторяющихся экземпляров было бы безвредным (хотя equals должен проверять оба идентификатора), но если объект Person инкапсулирует изменяемое состояние Person, фундаментальная корректность требует либо уникальности объекта, либо использования довольно сложных механизмов для поддержания согласованности [и в последнем случае equals лучше проверять не только ID!] - person supercat; 18.11.2013
comment
@supercat: рецепт катастрофы - может быть ... Я не уверен, что вы можете избежать создания другого экземпляра с помощью сериализации, но я согласен, что замена его как можно скорее важна. неизменяемая комбинация идентификатора человека и идентификатора базы данных, какой идентификатор базы данных? Без идентификатора БД это похоже на решение Криса Джестера-Янга, просто с использованием более причудливого Integer. И нет, тестирование чего-либо изменяемого в equals здесь не спасает от проблем; это может только вызвать много других. - person maaartinus; 18.11.2013
comment
@maaartinus: Единственный сценарий, который я вижу, в котором Person имело бы смысл быть изменчивым, это является сущностью; за относительно небольшим исключением сущности не могут быть сериализованы — только информация о них. Если бы кто-то расшифровал ваш генетический образец, теоретически было бы возможно создать человека из этого расшифрованного генетического образца, но этим человеком были бы не вы. Что касается идентификатора базы данных, возможно, мне следовало сказать, что Person может инкапсулировать любую информацию, необходимую для доступа к одному хранилищу информации, где-то представляющему человека. - person supercat; 18.11.2013
comment
@maaartinus: Альтернативный подход состоял бы в том, чтобы разные отделы содержали PersonPartialPay объектов и имели для каждого сотрудника список всех PersonPartialPay объектов, которые к ним прикреплены. В этом случае, если кто-то работает в нескольких отделах, у каждого может быть объект, который он может изменять по своему желанию, но объект, связанный с работой сотрудника № 12345 в почтовом отделении, будет отличаться от объекта, связанного с его работой в отделе расчета заработной платы. , и эти двое не должны считаться равными, даже если обе конторы платят ему одинаковую сумму. - person supercat; 18.11.2013
comment
@supercat: Разве мы не должны сократить нашу дискуссию? Теперь это становится немного философским. Оставляя в стороне PersonPartialPay, которые являются отдельными объектами, я использовал что-то вроде изменяемого сериализуемого Person с equals на основе идентификатора без интернирования. Это не идеально, но настолько хорошо, насколько это возможно. - person maaartinus; 18.11.2013
comment
@maaartinus: Если этот дизайн работает для вас, отлично, хотя для меня это не имеет смысла. На мой взгляд, объекты обычно должны либо инкапсулировать данные, и в этом случае должны быть равными только объекты, которые полностью совпадают, либо сущности, и в этом случае должны быть равными только объекты, представляющие одну и ту же сущность. Ваш объект звучит как странный гибрид, который я хотел бы разделить на отдельную часть сущности и часть данных, но, возможно, вы сможете обойтись без такого подразделения. - person supercat; 18.11.2013

Цитата означает, что неизменяемый класс может выбрать интерн его экземпляры. Это легко реализовать с помощью Interner, например:

public class MyImmutableClass {
    private static final Interner<MyImmutableClass> INTERN_POOL = Interners.newWeakInterner();
    private final String foo;
    private final int bar;

    private MyImmutableClass(String foo, int bar) {
        this.foo = foo;
        this.bar = bar;
    }

    public static MyImmutableClass of(String foo, int bar) {
        return INTERN_POOL.intern(new MyImmutableClass(foo, bar));
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(foo, bar);
    }

    @Override
    public boolean equals(Object o) {
        if (o == this)
            return true;        // fast path for interned instances
        if (o instanceof MyImmutableClass) {
            MyImmutableClass rhs = (MyImmutableClass) o;
            return Objects.equal(foo, rhs.foo)
                    && bar == rhs.bar;
        }
        return false;
    }
}

Здесь конструктор делается закрытым: все экземпляры должны проходить через метод фабрики MyImmutableClass.of(), который использует Interner, чтобы гарантировать, что если новый экземпляр является equals() по отношению к существующему экземпляру, вместо него будет возвращен существующий экземпляр.

Интернирование может использоваться только для неизменяемых объектов, под которыми я подразумеваю объекты, чье наблюдаемое состояние (т. е. поведение всех доступных извне методов, в частности equals() и hashCode()) не изменяется для объектов. жизни. Если вы интернируете изменяемые объекты, поведение будет неправильным при изменении экземпляра.

Как уже говорили многие другие люди, вы должны тщательно выбирать, какие объекты интернировать, даже если они неизменяемы. Делайте это только в том случае, если набор интернированных значений мал по сравнению с количеством дубликатов, которые у вас могут быть. Например, не стоит интернировать Integer вообще, потому что существует более 4 миллиардов возможных значений. Но стоит интернировать наиболее часто используемые Integer значения, и на самом деле Integer.valueOf() интернирует значения между -128 и 127. С другой стороны, перечисления отлично подходят для интернирования (а они интернированы по определению), потому что множество возможных значения небольшие.

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

person Chris Jester-Young    schedule 17.11.2013
comment
Да. Я хочу пояснений. Прочитав ваш ответ, я увидел blog.codecentric.de/en/2012/03/, и он говорит, что ВСЕ строки, которые жестко закодированы (как константы или где-либо в коде), автоматически интернируются компилятором. . Итак, мой вопрос: когда происходит интернирование строк? всегда? Это примечание косвенно дает какой-то намек, когда интернирования строк не произойдет? - person Harish Kayarohanam; 17.11.2013
comment
@Harish Kayarohanam: Автоматического процесса интернирования строк не существует. Просто все строки, которые появляются как константы в вашем исходном коде, уже начинают свою жизнь в промежуточном пуле. Всякий раз, когда вы пишете String a = "x", b = "x";, вы можете быть уверены, что a == b выполняется. То же самое для таких вещей, как Something.class.getName(), я думаю. Всякий раз, когда вы вызываете s.substring(), вы можете поспорить, что он не интернируется автоматически. - person maaartinus; 17.11.2013
comment
@Chris Jester-Young: я бы немного уточнил ваше утверждение об изменяемых классах. Это может работать до тех пор, пока ни одно поле, включенное в equals, не изменится (здесь нет необходимости говорить о hashCode, так как все поля в нем также должны быть включены в equals). Интернирование изменяемых объектов имеет ту же проблему, что и их использование в качестве ключей в коллекциях. - person maaartinus; 17.11.2013
comment
@maaartinus, не могли бы вы направить меня к некоторым видеоурокам или ресурсам, которые могут объяснить эти концепции интернирования и изменчивости? - person Harish Kayarohanam; 17.11.2013
comment
@Harish Kayarohanam: я сомневаюсь, что есть что-то кроме JLS. Обратите внимание, что стажировка не так крута, как кажется. У него есть стоимость, как указано в другом ответе, и он редко стоит того. Будьте предупреждены о преждевременной оптимизации. Возможно, вы захотите взглянуть на кеширование хэш-кода. - person maaartinus; 17.11.2013
comment
@maaartinus означает ли это, что интернирование действительно приносит пользу, когда количество экземпляров невелико? Как предположил Стивен, если имеется большое количество экземпляров, то сама хеш-таблица будет занимать столько памяти, и поиск также займет память. Прав ли я в своем умозаключении? - person Harish Kayarohanam; 17.11.2013
comment
Но из blog.codecentric .de/en/2012/03/ Я чувствую, что когда нужно создать больше экземпляров, интернирование значительно экономит место. его пример Из этого снимка экрана видно, что пустые строки занимают много памяти! 2 миллиона пустых строк занимают в общей сложности 130 МБ. 130 МБ — это много, верно? - person Harish Kayarohanam; 17.11.2013
comment
так что, наконец, два вывода в двух из приведенных выше комментариев противоречат друг другу. так когда стажировка выгодна? количество времени и памяти, потраченных на поиск, будет намного меньше, чем 130 МБ (для того же примера). Является ли это компромиссом, предполагающим, что стажировка выгодна как при меньшем количестве объектов, так и при большем количестве объектов? - person Harish Kayarohanam; 17.11.2013
comment
@Harish Kayarohanam: Конечно, вы можете сэкономить много памяти, если есть много одинаковых экземпляров. И вы тратите немного памяти и времени, если их всего несколько. Это довольно очевидно: чем больше экземпляр и чем больше количество одинаковых экземпляров, тем больше вы можете сэкономить. В большинстве случаев стажировка невыгодна, но в некоторых случаях может очень помочь. - person maaartinus; 17.11.2013
comment
@мааартинус . Вы можете привести примеры, когда стажировка невыгодна. Можете ли вы указать мне несколько примеров, чтобы это научило меня не использовать стажера плохо .. пожалуйста .. - person Harish Kayarohanam; 17.11.2013
comment
Я выразил сомнение здесь / как комментарий к его ответу - person Harish Kayarohanam; 17.11.2013
comment
@Harish Interning может быть полезен, если вселенная возможных значений ограничена и потенциально может быть много дубликатов. В этом случае интернирование оправдано для экономии памяти. Обычно речь идет не об экономии времени выполнения, и, как говорится во всех других комментариях, вы должны разумно подходить к тому, какие классы интернировать. Например, вы не должны интернировать Integer вообще (хотя для небольших значений, например, между -128 и 127, они интернируются, если вы используете метод фабрики Integer.valueOf(), но это еще один пример ограниченной вселенной). - person Chris Jester-Young; 17.11.2013
comment
@HarishKayarohanam Стажировка — очень простая концепция. Лучшее, что я могу сделать, чтобы научить вас, как не использовать его неправильно, — это просто сказать: не используйте его прямо сейчас. Характер ваших вопросов говорит о том, что вы не понимаете некоторых фундаментальных понятий и поэтому не готовы задавать эти вопросы. Если интернирование предмета имеет смысл, используйте его. Если нет, то не надо. Если вы не уверены, не делайте этого и вместо этого потратьте время на изучение фундаментальных понятий. Если вы не можете сделать логический вывод о его полезности, скорее всего, у вас возникнут более серьезные проблемы. - person Jason C; 17.11.2013
comment
Я имею в виду, что это все равно, что спросить, когда мне следует использовать оператор +? Ну, когда вы хотите добавить цифры, да. Это так очевидно, и если вам пришлось задать этот вопрос, значит, вы упускаете некоторые действительно фундаментальные концепции написания компьютерных программ и задали неправильный вопрос. - person Jason C; 17.11.2013

Если вы можете гарантировать, что не существует двух экземпляров объекта с эквивалентными семантическими значениями (т. е. если x и y относятся к разным экземплярам [x != y], то x.equals(y) == false для всех x и y), то это означает, что вы можете сравнивать две ссылки. ' объекты на равенство, просто проверяя, ссылаются ли они на один и тот же экземпляр, что и делает ==.

Реализация ==, по сути, просто сравнивает два целых числа (адреса памяти) и, как правило, будет быстрее, чем практически все нетривиальные реализации .equals().

Стоит отметить, что это не переход, который может быть выполнен для Strings, так как вы не можете гарантировать, что любые два экземпляра String не эквивалентны, например:

String x = new String("hello");
String y = new String("hello");

Поскольку x != y && x.equals(y) недостаточно просто выполнить x == y для проверки на равенство.

person Jason C    schedule 17.11.2013
comment
Если бы кто-то хотел иметь эту систему для строк, JVM нужно было бы переопределить, чтобы вызывать intern для всех новых объектов String (== работает для интернированных строк). - person Thilo; 17.11.2013
comment
Но ваш пример неверен, потому что x и y оба указывают на один и тот же строковый литерал, а строковые литералы всегда интернированы в Java. Если вы изменили свой пример, чтобы использовать new String("hello") по крайней мере для одной из этих переменных, то ваше утверждение о x != y действительно было бы верным. - person Chris Jester-Young; 17.11.2013
comment
Что я должен сделать из кода public class HelloWorld{ public static void main(String []args){ String x = new String(hello); Строка y = новая строка (привет); System.out.println(x == y); Строка а = привет; Строка b = привет; System.out.println(a == b); } } o/p false true почему интернирование не происходит в первом случае new String(). Это потому, что мы явно запрашиваем новый экземпляр, используя new String() ; - person Harish Kayarohanam; 17.11.2013
comment
да, у меня есть решение здесь. ntu.edu.sg/home/ehchua/programming/java/ J3d_String.html - person Harish Kayarohanam; 17.11.2013
comment
Даже минимальное знание объектов Java, языка и «нового» позволило бы вам прийти к такому выводу самостоятельно. Вам действительно нужно вернуться к основам языка, ознакомьтесь с официальными руководствами на сайте оракула. - person Jason C; 17.11.2013

Чтобы ответить на ваши вопросы...

Что он имеет в виду под улучшением производительности здесь [String]? Как это улучшает производительность.

Это НЕ пример того, о чем говорит Блох. Блох говорит о классах, управляемых экземпляром, а String таким классом не является!

Прав ли я в этом выводе?

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

Однако некоторые наблюдения:

  • Это относится только к неизменяемым объектам. Или точнее к объектам, где мутация не влияет на семантику равенства.

  • Это относится только к классам, полностью контролируемым экземпляром.

  • Контроль экземпляра может быть дорогим. Рассмотрим форму (частичного) управления экземпляром, предоставляемую методом intern класса String и пулом строк.

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

    • Каждый раз, когда вы интернируете String, он вычисляет хэш-код строки и проверяет хэш-таблицу, чтобы узнать, не была ли уже интернирована аналогичная строка.

    • Каждый раз, когда выполняется полный сборщик мусора, слабые ссылки в пуле строк приводят к дополнительной работе по «отслеживанию» для сборщика мусора, а затем, возможно, к дополнительной работе, если сборщик мусора решит сломать ссылки.

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

person Stephen C    schedule 17.11.2013

Я думаю, это означает следующее:

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

Но если из-за какой-то хитрости языка вы знали, что две сложные, но одинаковые структуры не могут существовать одновременно, то вместо проверки равенства путем их побитового сравнения вы можете просто проверить, что они находятся в одном и том же месте в памяти, и вернуть false, если они не.

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

person odedsh    schedule 17.11.2013

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

  • Это ссылки на один и тот же объект (очень быстро)

  • Это ссылки на разные объекты, которые инкапсулируют разные значения (часто быстрые, но иногда медленные).

  • Это ссылки на разные объекты, которые инкапсулируют одно и то же значение (обычно всегда медленное).

Если объекты чаще всего будут обнаруживаться равными, чем нет, то минимизация частоты случая 3 может иметь существенное значение. не бывает очень часто.

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

Подход, который можно было бы использовать, если вы не хотите использовать отдельную промежуточную коллекцию, состоял бы в том, чтобы каждый объект сохранял порядковый номер long, чтобы всегда можно было определить, какой из двух в остальном идентичных объектов был создан первым, вместе со ссылкой к самому старому объекту, который, как известно, содержит то же самое содержимое. Чтобы сравнить две ссылки, начните с определения самого старого объекта, который, как известно, эквивалентен каждой из них. Если самый старый объект, который, как известно, соответствует первому, не совпадает с самым старым объектом, который, как известно, соответствует второму, сравните содержимое объектов. Если они совпадают, один будет новее другого, и этот объект может рассматривать другой как «самый старый объект, который, как известно, соответствует».

person supercat    schedule 17.11.2013