平等还是认同?
將對象存儲在集合中時,同一對象永遠不能添加兩次非常重要。 這是集合的核心定義。 在Java中,使用兩種方法來確定兩個引用的對象是否相同或它們是否可以同時存在于同一Set中。 equals()和hashCode()。 在本文中,我將解釋平等與同一性之間的區別,并探討它們在彼此之間所具有的一些優勢。
Java提供了這兩種方法的標準實現。 標準的equals()方法被定義為“身份”比較方法。 這意味著它將比較兩個內存引用以確定它們是否相同。 因此,存儲在內存中不同位置的兩個相同的對象將被視為不相等。 如果使用Object類的源代碼,可以使用==-operator進行比較。
public boolean equals(Object obj) {return (this == obj); }hashCode()方法由虛擬機作為本機操作實現,因此在代碼中不可見,但通常將其實現為簡單地返回內存引用(在32位體系結構上)或以32位模表示內存引用(在64位體系結構上)。
許多程序員在設計類時選擇要做的一件事就是用不同的相等性定義覆蓋此方法,在該方法中,您不查看內存引用,而是查看兩個實例的值以查看它們是否可以相等。 這是一個例子:
import java.util.Objects; import static java.util.Objects.requireNonNull;public final class Person {private final String firstname;private final String lastname;public Person(String firstname, String lastname) {this.firstname = requireNonNull(firstname);this.lastname = requireNonNull(lastname);}@Overridepublic int hashCode() {int hash = 7;hash = 83 * hash + Objects.hashCode(this.firstname);hash = 83 * hash + Objects.hashCode(this.lastname);return hash;}@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (obj == null) return false;if (getClass() != obj.getClass()) return false;final Person other = (Person) obj;if (!Objects.equals(this.firstname, other.firstname)) {return false;} else return Objects.equals(this.lastname, other.lastname);} }這種比較稱為“平等”(與之前的“身份”相比)。 只要兩個人的名字和姓氏相同,就將被視為相等。 例如,這可以用于從輸入流中篩選出重復項。 請記住,如果您覆蓋equals()方法,則還應該始終覆蓋hashCode()方法!
平等
現在,如果您選擇平等而不是身份,那么您將需要考慮一些事項。 您必須問自己的第一件事是:具有相同屬性的此類的兩個實例是否一定相同? 對于上述人員,我會拒絕。 有一天,您的系統中很有可能會有兩個姓氏和名字相同的人。 即使您繼續添加更多的個人信息,例如生日或喜歡的顏色,您遲早也會發生沖突。 另一方面,如果您的系統正在處理汽車,并且每輛汽車都包含對“模型”的引用,則可以安全地假設,如果兩輛汽車都是黑色Tesla模型S,則即使對象是相同的,它們也可能是同一模型。存儲在內存中的不同位置。 這是平等可能很好的一個例子。
import java.util.Objects; import static java.util.Objects.requireNonNull;public final class Car {public static final class Model {private final String name;private final String version;public Model(String name, String version) {this.name = requireNonNull(name);this.version = requireNonNull(version);}@Overridepublic int hashCode() {int hash = 5;hash = 23 * hash + Objects.hashCode(this.name);hash = 23 * hash + Objects.hashCode(this.version);return hash;}@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (obj == null) return false;if (getClass() != obj.getClass()) return false;final Model other = (Model) obj;if (!Objects.equals(this.name, other.name)) {return false;} else return Objects.equals(this.version, other.version);}}private final String color;private final Model model;public Car(String color, Model model) {this.color = requireNonNull(color);this.model = requireNonNull(model);}public Model getModel() {return model;} }如果兩輛汽車的內存地址相同,則它們被認為是相同的。 另一方面,只要它們具有相同的名稱和版本,則認為它們的模型相同。 這是一個例子:
final Car a = new Car("black", new Car.Model("Tesla", "Model S")); final Car b = new Car("black", new Car.Model("Tesla", "Model S"));System.out.println("Is a and b the same car? " + a.equals(b)); System.out.println("Is a and b the same model? " + a.getModel().equals(b.getModel()));// Prints the following: // Is a and b the same car? false // Is a and b the same model? true身分識別
選擇平等而不是身份的一種風險是,它可能會導致分配比堆上更多的對象。 只需看上面的汽車示例。 對于我們創建的每輛汽車,我們還為模型分配內存中的空間。 即使Java通常優化了字符串分配以防止重復,對于始終相同的對象仍然是一定的浪費。 將內部對象轉換為可以使用身份比較方法進行比較并且同時避免不必要的對象分配的一個簡單技巧是將其替換為枚舉:
public final class Car {public enum Model {TESLA_MODEL_S ("Tesla", "Model S"),VOLVO_V70 ("Volvo", "V70");private final String name;private final String version;Model(String name, String version) {this.name = name;this.version = version;}}private final String color;private final Model model;public Car(String color, Model model) {this.color = requireNonNull(color);this.model = requireNonNull(model);}public Model getModel() {return model;} }現在我們可以確定每種模型只會在內存中的某個位置存在,因此可以使用身份比較安全地進行比較。 然而,與此有關的一個問題是,這實際上限制了我們的可擴展性。 在此之前,可以在不修改Car.java文件中的源代碼的情況下即時定義新模型的方法,但是現在我們已經將自己鎖定在一個枚舉中,該枚舉通常應該保持不變。 如果需要這些屬性,那么對等比較可能更適合您。
最后一點,如果您重寫了類的equals()和hashCode()方法,后來又想根據身份將其存儲在Map中,則始終可以使用IdentityHashMap結構。 即使equals()和hashCode()方法已被覆蓋,它也將使用內存地址來引用其鍵。
翻譯自: https://www.javacodegeeks.com/2016/03/equality-vs-identity.html
總結
- 上一篇: rube3xxx_Rube Goldbe
- 下一篇: 让人一看就笑喷的网名 让人一看就笑的昵称