zoukankan      html  css  js  c++  java
  • Java中正确使用hashCode() 和equals() 、==

      在java编程中,经常会遇到两个对象中某个属性的比较,常常会用到的方法有: == 、equals()。但是两者使用起来有什么区别呢?

    一、==

    java中的==是比较两个对象在JVM中的地址。比较好理解。看下面的代码:

    1  public class ComAddr{
    2     public static void main(String[] args) throws Exception {
    3         String s1 = "nihao";
    4         String s2 = "nihao";
    5         String s3 = new String("nihao");
    6         System.out.println(s1 == s2);    //    true
    7         System.out.println(s1 == s3);    //    false
    8     }
    9 }

    上述代码中:

      (1)s1 == s2为true,是因为s1和s2都是字符串字面值"nihao"的引用,指向同一块地址,所以相等。

      (2)s1 == s3为false,是因为通过new产生的对象在堆中,s3是堆中变量的引用,而是s1是指向字符串字面值"nihao"的引用,地址不同所以不相等。

    二、使用hashCode()和equals()

      hashCode()和equals()定义在Object类中,这个类是所有java类的基类,所以所有的java类都继承这两个方法。

      hashCode()方法被用来获取给定对象的唯一整数。这个整数被用来确定对象被存储在HashTable类似的结构中的位置。默认的,Object类的hashCode()方法返回这个对象存储的内存地址的编号【即32位jvm的内存地址】。

      重写默认的实现

    如果你不重写这两个方法,将几乎不遇到任何问题,但是有的时候程序要求我们必须改变一些对象的默认实现。

    来看看这个例子,让我们创建一个简单的类Employee

    public class Employee
    {
        private Integer id;
        private String firstname;
        private String lastName;
        private String department;
     
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getFirstname() {
            return firstname;
        }
        public void setFirstname(String firstname) {
            this.firstname = firstname;
        }
        public String getLastName() {
            return lastName;
        }
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
        public String getDepartment() {
            return department;
        }
        public void setDepartment(String department) {
            this.department = department;
        }
    }

    上面的Employee类只是有一些非常基础的属性和getter、setter.现在来考虑一个你需要比较两个employee的情形。

    public class EqualsTest {
        public static void main(String[] args) {
            Employee e1 = new Employee();
            Employee e2 = new Employee();
     
            e1.setId(100);
            e2.setId(100);
            //Prints false in console
            System.out.println(e1.equals(e2));
        }
    }

    毫无疑问,上面的程序将输出false,但是,事实上上面两个对象代表的是通过一个employee。真正的商业逻辑希望我们返回true。 
    为了达到这个目的,我们需要重写equals方法。 【Id属性的重写】

    public boolean equals(Object o) {
            if(o == null)
            {
                return false;
            }
            if (o == this)
            {
               return true;
            }
            if (getClass() != o.getClass())
            {
                return false;
            }
            Employee e = (Employee) o;
            return (this.getId() == e.getId());
    }

    在上面的类中添加这个方法,EauqlsTest将会输出true。 
    So are we done?没有,让我们换一种测试方法来看看。 

    import java.util.HashSet;
    import java.util.Set;
     
    public class EqualsTest
    {
        public static void main(String[] args)
        {
            Employee e1 = new Employee();
            Employee e2 = new Employee();
     
            e1.setId(100);
            e2.setId(100);
     
            //Prints 'true'
            System.out.println(e1.equals(e2));
     
            Set<Employee> employees = new HashSet<Employee>();
            employees.add(e1);
            employees.add(e2);
            //Prints two objects
            System.out.println(employees);
        }

    上面的程序输出的结果是两个。如果两个employee对象equals返回true,Set中应该只存储一个对象才对,问题在哪里呢? 
    我们忘掉了第二个重要的方法hashCode()。就像JDK的Javadoc中所说的一样,如果重写equals()方法必须要重写hashCode()方法。我们加上下面这个方法,程序将执行正确。

    @Override
     public int hashCode()
     {
        final int PRIME = 31;
        int result = 1;
        result = PRIME * result + getId();
        return result;
     }

    重写hashCode()和equals()的补充:

      1、使用Apache Commons Lang包重写hashCode() 和equals()方法 
    Apache Commons 包提供了两个非常优秀的类来生成hashCode()和equals()方法。看下面的程序。

    import org.apache.commons.lang3.builder.EqualsBuilder;
    import org.apache.commons.lang3.builder.HashCodeBuilder;
    public class Employee
    {
     private Integer id;
     private String firstname;
     private String lastName;
     private String department;
    public Integer getId() {
        return id;
     }
     public void setId(Integer id) {
        this.id = id;
     }
     public String getFirstname() {
        return firstname;
     }
     public void setFirstname(String firstname) {
        this.firstname = firstname;
     }
     public String getLastName() {
        return lastName;
     }
     public void setLastName(String lastName) {
        this.lastName = lastName;
     }
     public String getDepartment() {
        return department;
     }
     public void setDepartment(String department) {
        this.department = department;
     }
    @Override
     public int hashCode()
     {
        final int PRIME = 31;
        return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).
               toHashCode();
     }
    @Override
     public boolean equals(Object o) {
        if (o == null)
           return false;
        if (o == this)
           return true;
        if (o.getClass() != getClass())
           return false;
        Employee e = (Employee) o;
           return new EqualsBuilder().
                  append(getId(), e.getId()).
                  isEquals();
        }
     }

    2、如果你使用Eclipse或者其他的IDE,IDE也可能会提供生成良好的hashCode()方法和equals()方法。 

    注意点

    需要注意记住的事情

    • 尽量保证使用对象的同一个属性来生成hashCode()和equals()两个方法。在我们的案例中,我们使用员工id。
    • eqauls方法必须保证一致(如果对象没有被修改,equals应该返回相同的值)
    • 任何时候只要a.equals(b),那么a.hashCode()必须和b.hashCode()相等。
    • 两者必须同时重写。

    当使用ORM的时候特别要注意的

    • 如果你使用ORM处理一些对象的话,你要确保在hashCode()和equals()对象中使用getter和setter而不是直接引用成员变量。因为在ORM中有的时候成员变量会被延时加载,这些变量只有当getter方法被调用的时候才真正可用。
    • 例如在我们的例子中,如果我们使用e1.id == e2.id则可能会出现这个问题,但是我们使用e1.getId() == e2.getId()就不会出现这个问题。
  • 相关阅读:
    leetcode701. Insert into a Binary Search Tree
    leetcode 958. Check Completeness of a Binary Tree 判断是否是完全二叉树 、222. Count Complete Tree Nodes
    leetcode 110. Balanced Binary Tree
    leetcode 104. Maximum Depth of Binary Tree 111. Minimum Depth of Binary Tree
    二叉树
    leetcode 124. Binary Tree Maximum Path Sum 、543. Diameter of Binary Tree(直径)
    5. Longest Palindromic Substring
    128. Longest Consecutive Sequence
    Mac OS下Android Studio的Java not found问题,androidfound
    安卓 AsyncHttpClient
  • 原文地址:https://www.cnblogs.com/renxiaoren/p/5590827.html
Copyright © 2011-2022 走看看