Когда вызовы метода HashSet 'add' равны?

Я провел этот тест при сравнении HashSet, и equals не вызывается

Я хотел бы считать равным, когда farAway = false (функция для проверки двухточечных расстояний)

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

public class TestClass{
     static class Posicion
    {
        private int x;
        private int y;

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final Posicion other = (Posicion) obj;
            if ( farAway(this.x, other.x, this.y, other.y,5)){   
                return false;
            } 
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7; hash = 59 * hash + this.x; hash = 59 * hash + this.y;
            return hash;
        }

         Posicion(int x0, int y0) {
            x=x0;
            y=y0;
        }

        private boolean farAway(int x, int x0, int y, int y0, int i) {
            return false;
        }
    }

    public static void main(String[] args) {
        HashSet<Posicion> test=new HashSet<>();
        System.out.println("result:"+test.add(new Posicion(1,1)));
        System.out.println("result:"+test.add(new Posicion(1,2)));
    }
}

ИЗМЕНИТЬ

-Есть ли способ принудительно добавить HashSet для вызова равенства?


person Hernán Eche    schedule 24.01.2013    source источник
comment
что это за вызов farAway внутри equals ?? ваш класс нарушает договор между hashCode и equals!   -  person Alonso Dominguez    schedule 24.01.2013
comment
@AlonsoDominguez Здесь у вас есть полный тест, farAway просто возвращает false, чтобы гарантировать, что объект будет рассматриваться как равный, но equals никогда не вызывается.   -  person Hernán Eche    schedule 24.01.2013
comment
вы должны соблюдать условия контракта между hashCode и equals. Это означает, что когда ваш equals метод возвращает true, вы hashCode должны возвращать то же целочисленное значение. А в вашем коде этого не происходит.   -  person Alonso Dominguez    schedule 24.01.2013
comment
@AlonsoDominguez Я понимаю, что пытался избежать hashCode и использовать только равные, теперь я вижу, что это невозможно, спасибо, что настаиваете на разрыве контракта, тогда теперь я знаю, когда equals не вызывается, я до сих пор не знаю, когда equals на самом деле вызывается.   -  person Hernán Eche    schedule 24.01.2013
comment
@ HernánEche: то, что вы, кажется, хотите сделать, может оказаться очень сложным - вы должны соблюдать не только контракт hashCode(), но и контракт equals(). equals() должен быть переходным. Поэтому, если вы вернете true для A и B, потому что они близки друг к другу, а B и C, потому что они близки друг к другу, вы столкнетесь с проблемами при проверке равенства между A и C, если они не близки. Я думаю, вам нужно будет разработать структуру контейнера / данных, отличную от стандартного HashSet, чтобы делать то, что вы хотите (если то, что вы хотите сделать, это вообще хорошая идея).   -  person Michael Burr    schedule 24.01.2013
comment
@MichaelBurr Bold insight +1 (и забавная скобка), хорошая мысль, евклидово расстояние не будет транзитивным, если поставить любой порог, A может быть «около» B, а B - «около» C, но мы ничего не можем сказать о C что касается A, он может быть близким или нет, тогда равный должен сравниваться со всеми элементами (что-то не так). Я знал об этом, это не нарушает мой код, потому что здесь порядок загрузки, и это не евклидово расстояние, но это очень полезный комментарий, спасибо   -  person Hernán Eche    schedule 25.01.2013


Ответы (4)


Если хеш-коды различаются, нет необходимости вызывать equals(), поскольку он гарантированно вернет false.

Это следует из общего контракта на equals() и hashCode():

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

Прямо сейчас ваш класс нарушает этот контракт. Тебе нужно это исправить.

person NPE    schedule 24.01.2013
comment
Мне нужны равные, чтобы меня называли - person Hernán Eche; 24.01.2013
comment
Тогда хэш-код не должен быть таким же. - person RoflcoptrException; 24.01.2013
comment
Вот в чем вопрос, hashCode не то же самое, и equals не вызывается. - person Hernán Eche; 24.01.2013
comment
@ HernánEche У вас уже есть предмет в вашем наборе? Если вы добавите первый, equals может не вызываться (так как его не с чем сравнивать). - person Sebastian Krysmanski; 24.01.2013
comment
@ HernánEche: это бессмысленно. HashSet вызывает equals (), чтобы убедиться, что в наборе не хранятся повторяющиеся объекты. Если он может гарантировать это без вызова equals (например, потому что в наборе еще ничего нет или потому что ни один другой объект в наборе не имеет такого же хэш-кода), зачем ему вызывать equals ()? А с каким другим объектом? - person JB Nizet; 24.01.2013
comment
@SebastianKrysmanski да, конечно, вы можете протестировать этот пример. - person Hernán Eche; 24.01.2013
comment
@ HernánEche: два объекта возвращают разные хэш-коды. По контракту на hashCode() библиотека (или кто-либо) может принять это как означающее, что объекты не равны - нет необходимости вызывать equals(), если хэш-коды различны. - person Michael Burr; 24.01.2013
comment
@MichaelBurr Дело в том, что если хэш-коды различны, тогда нет необходимости называть равные, а если хэш-коды равны, тогда нет необходимости вызывать равные, наконец, equals никогда не вызывается, поэтому ответ на этот вопрос должен быть, равно никогда вызывается, независимо от того, что вы помещаете в него или в хэш-код - person Hernán Eche; 24.01.2013
comment
@ HernánEche: Это неправда. Сделайте hashCode() всегда возвращать 0 и посмотрите, что произойдет. - person NPE; 24.01.2013
comment
@ Hernán: Если хэш-коды разные, объекты разные и равенство не вызывается. Если хэш-коды идентичны, объекты могут быть равными, поэтому вызывается equals (Object), чтобы гарантировать, что они одинаковы. - person jarnbjo; 24.01.2013
comment
@jarnbjo, ты прав, я не тестировал, спасибо! - person Hernán Eche; 24.01.2013

Если вы хотите, чтобы equals() вызывалась всегда, просто всегда возвращайте, скажем, 0 в hashCode(). Таким образом, все элементы имеют одинаковый хэш-код и сравниваются только с equals().

public int hashCode() {
  return 0;
}
person Sebastian Krysmanski    schedule 24.01.2013
comment
Тогда не могли бы вы объяснить, какой результат вы ожидаете? Какой результат должен выдавать ваш код? - person Sebastian Krysmanski; 24.01.2013
comment
Функциональность отклоняется, добавляются два похожих объекта, в данном случае две точки на плоскости, которые находятся близко друг к другу, но я могу достичь той же функциональности сотнями способов, вопрос не в решении одной проблемы, а в том, чтобы понять < b> when equals фактически вызывается HashMap, или if equals никогда не вызывается через него. - person Hernán Eche; 24.01.2013
comment
Я только что протестировал свой код, и он работает должным образом. Вызов test.add(new Posicion(1,2) возвращает false, потому что вызывается equals(). Что в этом плохого? - person Sebastian Krysmanski; 24.01.2013
comment
Мой тест был неправильным, теперь все ясно, спасибо - person Hernán Eche; 24.01.2013
comment
Есть ли недостатки в использовании этого трюка? - person optimusfrenk; 19.03.2015

Похоже, HashSet вам не подходит. Похоже, вам нужен собственный способ сравнения двух позиций. Вместо того, чтобы говорить «две позиции в точности равны?». Вместо этого вам следует использовать TreeSet с компаратором. Таким образом, вы можете написать «IsWithinRangeComparator» и выполнять там проверку диапазона.

person David Lavender    schedule 24.01.2013

Как было предложено выше, когда объекты равны, их хэш-код также должен быть одинаковым. Вы можете просто исправить вычисление хэш-кода, как показано ниже.

 public int hashCode() {

int hash = 7; hash = 59 * hash + this.x; hash = 59 * hash + this.y;
boolean faraway=farAway(this.x, other.x, this.y, other.y,5);
hash=59*hash+(faraway?1:0); //include faraway also as part of hashcode computation

 return hash;

}

person Zenil    schedule 24.01.2013
comment
Конечно, невозможно использовать метод farAway из hashCode (), поскольку у вас нет другого объекта для сравнения. - person jarnbjo; 24.01.2013
comment
@herman: Hashet работает, сравнивая хэш-код объекта. Если хэш-коды разные, тогда нет необходимости вызывать equals (), потому что объекты считаются разными, и оба объекта будут помещены в хэш-набор. equals () вызывается только в том случае, если хэш-коды совпадают, и в этом случае equals () будет вызываться для сравнения значений - person Zenil; 24.01.2013
comment
@Zenil нет, если хэш-коды одинаковы, equals не будет вызываться, просто потому, что это означает, что объекты такие же!, Тогда мне все еще интересно, когда вызывается equals? - person Hernán Eche; 24.01.2013
comment
@ herman, @ jarnbbjo: faraway () принимает 5 аргументов. Зачем вам нужен другой объект для сравнения? Вам нужны x и y других объектов, которые передаются. Faraway () может вызываться в хэш-коде так же, как он вызывается в equals (). Я что-то упускаю? - person Zenil; 24.01.2013
comment
@Zenil: Да, тебе чего-то не хватает. Откуда вы хотите взять аргументы other.x и other.y, если у вас нет другого объекта? - person jarnbjo; 24.01.2013
comment
@jarnbjo: Понятно. Спасибо - person Zenil; 24.01.2013