對象相等比較

String的相等比較

對於String類型而言,一般用“==”或者equales做相等比較,前者比較字符串的引用,後者比較字符串的值。

字符串常量的值存儲於常量池中,只要值相同,那麼引用的就是同一個字符串常量,也就是說,==和equals效果一樣。

字符串對象存儲於堆中,不同的對象在堆上的內存地址是不一樣的。因此,創建兩個值相等的String對象,其引用的地址是不相等的。也就是說,用 == 比較時不等,用equals比較時相等。

Integer的相等比較

Integer是int的包裝類型,通常直接賦值即可。比較時用==還是equals呢?看下面的例子:

Integer t1 = 129;
Integer t2 = 129;
Integer t3 = 119;
Integer t4 = 119;
System.out.println(t1 == t2);
System.out.println(t3 == t4);

輸出結果是false,true。

原因是:對於在-128~127之間的數,Integer使用的是內部緩存值,在此範圍之外的數,Integer會在堆上創建對象。t1和t2是堆上的不同對象,所以引用不相等。而t3和t4是內部緩存的值,引用相等。

顯式new出來的Integer對象,必然會在堆上創建對象,其引用是不同的。
Integer和int進行==比較時,會自動拆箱進行值的比較。

HashMap中key的相等比較

對於HashMap的key,如果是基本數據類型,則直接進行值的比較。而如果是引用類型呢?

HashMap底層是用數組和鏈表存儲的,當鏈表長度超過8時會轉化爲紅黑樹。根據key進行查找時,先根據hashCode找到數組上的位置,如果該位置上有多個元素,則繼續通過equals方法判斷值是否相等。

當兩個複雜對象作爲map的key相等時,必然先要保證hashCode相同,其次equals也要爲true。也就是說,使用複雜對象作爲map的key時,需要重寫hashCode和queals方法。

如果只重寫hashCode方法不重寫equals方法,則默認的equals方法會進行內存地址的比較,必然是不同的。如果不重寫hashCode方法而僅僅重寫equals方法,則首先數組上的位置就不同(當然,發生哈希碰撞時例外),對象自然不會相同。

看下面的例子:

public static class Student {

    private String name;

    private String id;

    public Student(String name) {
        this.name = name;
    }

    public Student(String name, String id) {
        this.name = name;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

}

public static void main(String[] args) throws Exception {
    Student stu1 = new Student("aa", "1");
    Student stu2 = new Student("aa", "1");
    Map<Student, String> map = new HashMap<>();
    map.put(stu1,"1");
    map.put(stu2,"2");
    System.out.println(map.size());
}

輸出爲2。

在這個例子中,map中放入Student類型對象作爲key,如果不重寫比較方法的話,放入的對象是不同的(默認的比較方法繼承於Object類,比較的是內存地址)。

重寫hashCode和queals

對Student類的比較方法進行重寫:

public static class Student {

    private String name;

    private String id;

    public Student(String name) {
        this.name = name;
    }

    public Student(String name, String id) {
        this.name = name;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof Student)) {
            return false;
        }
        Student s = (Student) o;
        return Objects.equals(id, s.getId()) && Objects.equals(name, s.getName());
    }

}

public static void main(String[] args) throws Exception {
    Student stu1 = new Student("aa", "1");
    Student stu2 = new Student("aa", "1");
    Map<Student, String> map = new HashMap<>();
    map.put(stu1,"1");
    map.put(stu2,"2");
    System.out.println(map.size());
}

輸出爲1,說明stu2和stu1相同,添加元素的key相同時,後者覆蓋了前者。

還可以用Apache Commons Lang中的API來實現重寫:

@Override
public int hashCode() {
    return new HashCodeBuilder(17, 37).append(name).append(id).toHashCode();
}

@Override
public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof Student)) {
        return false;
    }
    Student s = (Student) o;
    return new EqualsBuilder().append(name, s.getName()).append(id, s.getId()).isEquals();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章