Java基础之(二十五)hashCode() 与 toString()

    xiaoxiao2025-08-10  9

    hashCode()方法

    我们在程序中输入任意长度的二进制数据,通过hash算法就能产生对应的唯一的散列值。 hash算法的另一个作用是存储数据,假设我们要存储四个数据,通过hash算法,我们为每个数据产生一个不同的散列值,然后我们把散列值和数据存储的位置建立一种关联。因此,当你下次想要找到某个数据时,只需要知道hash值就知道了它存储的位置。

    散列值我们可以理解为房子上的门牌号,我们想要找某个人,知道了门牌号就可以找到。

    说明 说hashCode之前,先来看看Object类。

    我们知道,Object类是java程序中所有类的直接或间接父类,处于类层次的最高点。在Object类里定义了很多我们常见的方法,包括我们要讲的hashCode方法,如下:

    public final native Class<?> getClass(); public native int hashCode(); public boolean equals(Object obj) { return (this == obj); } public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }

    根据这个方法的声明可知,该方法返回一个int类型的数值,也就是说你调用某个对象的hashCode()之后,会产生一个这个对象的hash码。

    在java的很多类中都会重写equals和hashCode方法,如果两个对象用equals()方法进行比较,且结果相等,那么这两个对象调用hashCode()方法产生的hash码也是相等的。但是反过来说,equals()方法不相等的两个对象,hashCode()却是有可能相等的。

    看下面一个例子:

    class User{ String name; int age; public User(){} public User(String name,int age){ this.name = name; this.age = age; } public boolean equals(Object obj){ if(this == obj){ return true; } boolean b = obj instanceof User; if(b){ User u = (User)obj; if(this.age == u.age && this.name.equals(u.name)){ return true; } else{ return false; } } else{ return false; } } }

    下面再写一个测试类:

    import java.util.*; import java.util.HashMap; class Test{ public static void main(String[] args){ User u = new User("zhangsan",12); HashMap<User,String> map = new HashMap<User,String>(); map.put(u,"abc"); String s = map.get(new User("zhangsan",12)); System.out.println(s); } }

    按理说结果应该输出为“abc”,但是实际结果却是null。

    map根据传入的键(u)来寻找对应的值,它通过计算这个对象的hashCode来判断在hashMap当中有没有存在这个值。而我们并没有复写hashCode()方法,因此User对象用的是默认的hashCode方法。默认的hashCode方法对于内存中不同的对象生成的hash码是不一样的。

    hashCode()的复写

    那么,接下来我们就来重写hashCode()方法:

    class User{ String name; int age; public User(){} public User(String name,int age){ this.name = name; this.age = age; } public boolean equals(Object obj){ if(this == obj){ return true; } boolean b = obj instanceof User; if(b){ User u = (User)obj; if(this.age == u.age && this.name.equals(u.name)){ return true; } else{ return false; } } else{ return false; } } public int hashCode(){ //返回一个固定值,所有对象调用hashCode方法生成的hash码都是一样的 return 12; } } 输出结果: abc

    虽然得到了想要的结果,但是上面程序中复写的方法是不合理的。 下面这段话摘自Effective Java一书:

    在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一地返回同一个整数。

    如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。

    如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。

    对于第二条和第三条很好理解,但是第一条,很多时候就会忽略。在《Java编程思想》一书中的P495页也有同第一条类似的一段话: “设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果在讲一个对象用put()添加进HashMap时产生一个hashCdoe值,而用get()取出时却产生了另一个hashCode值,那么就无法获取该对象了。所以如果你的hashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码”。

    hashCode()方法的重写和equals()方法的重写往往是一起的。因此我们可以这样重写hashCode()方法。

    public int hashCode(){ //声明一个整型数据,值随便,一般是个质数 int result = 17; //这里习惯用31这个奇素数 result = 31*result + age; //name是String类型,String已经重写过hashCode(),这里生成它自己的hash码 result = 31*result + name.hashCode(); return result; }

    打印对象和toString()方法

    先看下面程序:

    class Person { private String name; public Person(String name) { this.name = name; } public void info() { System.out.println("此人名为:" + name); } } public class PrintObject { public static void main(String[] args) { //创建一个Person对象,将之赋给给p变量 Person p = new Person("孙悟空"); //打印p所引用的Person对象 System.out.println(p); } }

    上面程序创建了一个Person对象,然后使用System.out.println(p)方法输出Person对象。

    输出结果: Person@24ee5d13

    这个奇怪的结果是怎么来的呢?System.out.println方法只能在控制台输出字符串,,当使用该方法输出Person对象的时候,实际上是输出的Person对象的toString()方法的返回值,也就是说下面两行效果一样:

    System.out.println(p); System.out.println(p.toString());

    toString()方法是Object类里的一个实例方法,因此所有对象都具有toString()方法。 不仅如此,java对象都可以和字符串进行连接运算,当java对象都可以和字符串进行连接运算时,系统自动调用java对象toString方法的返回值和字符串进行连接运算,即下面两行效果一样:

    String pStr = p + " "; String pStr = p.toString() + " ";

    toString()方法是一个非常特殊的方法,它是一个“自我描述”方法,该方法通常用于实现这样的功能:当程序员直接打印该对象时,系统将会输出该对象的“自我描述”信息,用以告诉外界该对象具有的状态信息。

    Object类提供的toString()方法只是返回该对象实现类的类名+@+hashCode值,这个返回值并不能真正实现“自我描述“的功能,必须重写toString方法。

    class Apple { private String color; private double weight; public Apple(){ } //提供有参数的构造器 public Apple(String color , double weight) { this.color = color; this.weight = weight; } public void setColor(String color) { this.color = color; } public String getColor() { return this.color; } public void setWeight(double weight) { this.weight = weight; } public double getWeight() { return this.weight; } public String toString() { return "一个苹果,颜色是:" + color + ",重量是:" + weight; } } public class TestToString { public static void main(String[] args) { Apple a = new Apple("红色" , 5.68); //打印apple对象 System.out.println(a); } } 输出结果: 一个苹果,颜色是:红色,重量是:5.68

    从上面的结果可以看出,通过重写Apple类的toString方法,就可以让系统在打印Apple对象时打印出该对象的“自我描述”信息。

    大部分时候,重写toString方法总是返回该对象所有令人感兴趣的信息所组成的字符串,通常可返回如下格式字符串: 类名[属性1 = 值1,属性2 = 值2,…] 因此可将上面Apple类的toString方法改写如下:

    public String toString() { return "Apple[color=" + color + ",weight=" + weight + "]"; }

    关于hashCode更多的介绍呢,可以查看以下链接进行学习:

    http://www.cnblogs.com/dolphin0520/p/3681042.html

    http://blog.csdn.net/jiangwei0910410003/article/details/22739953

    http://www.tuicool.com/articles/fuE3Y3

    转载请注明原文地址: https://ju.6miu.com/read-1301616.html
    最新回复(0)