mongo db_name --eval "db.user_info.find().forEach(function(o) {print(o._id);})"
equals()
( javadoc) должен определить отношение эквивалентности (это должно быть рефлексивно , симметричный , и переходный ). Кроме того, это должно быть последовательно (если объекты не изменяются, то это должно продолжать возвращать то же значение). Кроме того, o.equals(null)
должен всегда возвращать false.
hashCode()
( javadoc) должен также быть последовательны (если объект не изменяется с точки зрения equals()
, это должно продолжать возвращать то же значение).
отношение между этими двумя методами:
Каждый раз, когда
a.equals(b)
, тогдаa.hashCode()
должно быть то же какb.hashCode()
.
, Если Вы переопределяете один, тогда необходимо переопределить другой.
Использование тот же набор полей, которые Вы используете для вычисления equals()
для вычисления hashCode()
.
Использование превосходные классы помощника EqualsBuilder и HashCodeBuilder от библиотека Commons Lang Apache. Пример:
public class Person {
private String name;
private int age;
// ...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
// if deriving: appendSuper(super.hashCode()).
append(name).
append(age).
toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder().
// if deriving: appendSuper(super.equals(obj)).
append(name, rhs.name).
append(age, rhs.age).
isEquals();
}
}
При использовании основанного на хеше Набор или Карта такой как [1 117] HashSet, LinkedHashSet, HashMap, Хеш-таблица , или WeakHashMap, удостоверяется, что хэш-код () ключевых объектов, которые Вы помещаете в набор, никогда не изменяется, в то время как объект находится в наборе. Пуленепробиваемый способ гарантировать это состоит в том, чтобы сделать Ваши ключи неизменными, , который обладает также другими преимуществами .
Существуют некоторые проблемы, которые стоит заметить, имеете ли Вы дело с классами, которые сохраняются с помощью Картопостроителя Объектных Отношений (ORM), любят, в спящем режиме, если Вы не думали, что это уже было необоснованно сложно!
Ленивые загруженные объекты являются подклассами
, Если Ваши объекты будут сохранены с помощью ORM, во многих случаях Вы будете иметь дело с динамическими прокси для предотвращения загружаемого объекта слишком рано от хранилища данных. Эти прокси реализованы как подклассы Вашего собственного класса. Это означает, что this.getClass() == o.getClass()
возвратится false
. Например:
Person saved = new Person("John Doe");
Long key = dao.save(saved);
dao.flush();
Person retrieved = dao.retrieve(key);
saved.getClass().equals(retrieved.getClass()); // Will return false if Person is loaded lazy
, Если Вы имеете дело с ORM, с помощью o instanceof Person
, единственная вещь, которая будет вести себя правильно.
Ленивые загруженные объекты имеют пустые поля
, ORMs обычно используют методов get для принуждения загрузки ленивых загруженных объектов. Это означает, что person.name
будет null
, если person
будет ленив загруженный, даже если person.getName()
загрузка сил и возвращает "John Doe". По моему опыту, это неожиданно возникает чаще в hashCode()
и equals()
.
, Если Вы имеете дело с ORM, удостоверьтесь, что всегда использовали методов get, и никогда ссылки поля в [1 111] и equals()
.
Сохранение объекта изменит свое состояние
, Постоянные объекты часто используют id
поле для удержания клавиши объекта. Это поле будет автоматически обновлено, когда объект будет сначала сохранен. Не используйте идентификационное поле в [1 114]. Но можно использовать его в [1 115].
шаблон А, который я часто использую,
if (this.getId() == null) {
return this == other;
}
else {
return this.getId().equals(other.getId());
}
, Но: Вы не можете включать getId()
в [1 117]. Если Вы делаете, когда объект сохраняется, hashCode
изменения. Если объект будет в HashSet
, Вы никогда не будете находить его снова.
В моем Person
пример, я, вероятно, использовал бы getName()
для [1 122] и getId()
плюс [1 124] (только для паранойи) для [1 125]. Это хорошо, если существует некоторый риск "коллизий" для [1 126], но никогда хорошо для [1 127].
hashCode()
должен использовать не изменяющееся подмножество свойств от [1 129]
Для благоприятной для наследования реализации проверьте решение Tal Cohen, , Как я Правильно Реализую равняние () Метод?
Сводка:
В его книге Эффективное Руководство Языка программирования Java (Addison-Wesley, 2001), Joshua Bloch утверждает, что "Нет просто никакого способа расширить instantiable класс и добавить аспект, в то время как сохранение равняется контракту". Tal не соглашается.
Его решение состоит в том, чтобы реализовать, равняется () путем называния другого несимметричного blindlyEquals () обоими путями. blindlyEquals () переопределяется подклассами, равняется (), наследован, и никогда не переопределяется.
Пример:
class Point {
private int x;
private int y;
protected boolean blindlyEquals(Object o) {
if (!(o instanceof Point))
return false;
Point p = (Point)o;
return (p.x == this.x && p.y == this.y);
}
public boolean equals(Object o) {
return (this.blindlyEquals(o) && o.blindlyEquals(this));
}
}
class ColorPoint extends Point {
private Color c;
protected boolean blindlyEquals(Object o) {
if (!(o instanceof ColorPoint))
return false;
ColorPoint cp = (ColorPoint)o;
return (super.blindlyEquals(cp) &&
cp.color == this.color);
}
}
Примечание, которое равняется () должно работать через иерархии наследования, если принцип замены Лисков должен быть удовлетворен.
Один глюк, который я нашел, - то, где два объекта содержат ссылки друг на друга (один пример, являющийся родительскими/дочерними отношениями с удобным методом для родителя для получения всех детей).
Эти виды вещей довольно распространены, когда выполнение В спящем режиме отображения, например.
, Если Вы включаете оба конца отношений в Вашем хэш-коде или равняетесь тестам, возможно войти в рекурсивный цикл, который заканчивается в StackOverflowException.
простое решение не должно включать getChildren набор в методы.
Существует несколько способов сделать Вашу проверку на равенство класса прежде, чем проверить членское равенство, и я думаю, что оба полезны при правильных обстоятельствах.
instanceof
оператор. this.getClass().equals(that.getClass())
. я использую № 1 в final
, равняется реализации, или при реализации интерфейса, который предписывает, чтобы алгоритм для равнялся (как java.util
набор interfaces— правильный способ свериться с (obj instanceof Set)
или безотносительно интерфейса Вы реализуете). Это - обычно плохой выбор, когда равняется, может быть переопределен, потому что это повреждает свойство симметрии.
Опция № 2 позволяет классу быть безопасно расширенным без переопределения, равняется или повреждающаяся симметрия.
, Если Ваш класс также Comparable
, equals
и compareTo
, методы должны быть последовательными также. Вот шаблон для, равняется методу в Comparable
класс:
final class MyClass implements Comparable<MyClass>
{
…
@Override
public boolean equals(Object obj)
{
/* If compareTo and equals aren't final, we should check with getClass instead. */
if (!(obj instanceof MyClass))
return false;
return compareTo((MyClass) obj) == 0;
}
}
Разъяснение о obj.getClass() != getClass()
.
Этот оператор является результатом equals()
являющийся недружелюбным наследованием. JLS (спецификация языка Java) определяет это, если A.equals(B) == true
тогда B.equals(A)
должен также возвратиться true
. Если Вы опустите тот оператор наследующие классы, которые переопределяют equals()
(и изменяются, то его поведение) повредит эту спецификацию.
Рассматривают следующий пример того, что происходит, когда оператор опущен:
class A {
int field1;
A(int field1) {
this.field1 = field1;
}
public boolean equals(Object other) {
return (other != null && other instanceof A && ((A) other).field1 == field1);
}
}
class B extends A {
int field2;
B(int field1, int field2) {
super(field1);
this.field2 = field2;
}
public boolean equals(Object other) {
return (other != null && other instanceof B && ((B)other).field2 == field2 && super.equals(other));
}
}
Выполнение new A(1).equals(new A(1))
кроме того, new B(1,1).equals(new B(1,1))
результат выделяет верный, как это должно.
Это выглядит все очень хорошим, но посмотрите, что происходит, если мы пытаемся использовать оба класса:
A a = new A(1);
B b = new B(1,1);
a.equals(b) == true;
b.equals(a) == false;
, Очевидно, это неправильно.
, Если Вы хотите гарантировать симметричное условие. a=b, если b=a и вызов принципа замены Лисков super.equals(other)
не только в случае [1 113] экземпляр, но и проверка после на [1 114] экземпляр:
if (other instanceof B )
return (other != null && ((B)other).field2 == field2 && super.equals(other));
if (other instanceof A) return super.equals(other);
else return false;
, Который произведет:
a.equals(b) == true;
b.equals(a) == true;
, Где, если a
не ссылка [1 116], то это могло бы быть быть ссылкой класса A
(потому что Вы расширяете его), в этом случае Вы звоните super.equals()
также .
Для равняется, изучите , Секреты Равняются Angelika Langer . Я люблю его очень. Она - также большой FAQ приблизительно Дженерики в Java. Просмотрите ее другие статьи здесь (прокрутите вниз к "Базовому Java"), где она также продолжает Часть 2 и "смешанное сравнение типов". Весело проведите время читая их!