龙空技术网

JAVA通用方法规约 equals hashCode等

高级JAVA指南 707

前言:

现时朋友们对“java通用方法”都比较注意,同学们都需要了解一些“java通用方法”的相关内容。那么小编同时在网摘上收集了一些关于“java通用方法””的相关文章,希望姐妹们能喜欢,咱们一起来学习一下吧!

本文主要讲JAVA中的通用方法规约。内容主要取自JDK源码注释和Effective Java(作者是Joshua Bloch大神,jdk中的集合java.util.Collection, java.util.Map等就是他的杰作,如果对java性能优化方面感兴趣的同学建议读一下这本书)。

java中Object的equals,hashcode,clone,toString方法,Comparable.compareTo方法应该遵从一定的规约,这有利于数据的存取、格式化、排序等,关于这些方法的规约在JDK源码注释中都有描述。

你面试时是否有人问你Object的hashcode方法返回的是什么?

HashMap中数据的存取原理?

如何降低哈希冲突的概率 ?

假如有个javaBean里面有两个关键字段userGroupId和userId那么应该如何实现其hashcode方法和equals方法?

Double、String、数组的hashcode是如何实现的?

对于刚入门java的同学被问到这样的问题时是否曾一脸懵逼?哈哈!开个玩笑,下面步入正题。

1. Object.equals(Object)

equals方法用于判断某个实例是否与当前实例逻辑相等。

无需重写equals的场景:

1) 类的每个实例本身是唯一的:如Thread,Enum

2) 父类已经重写了equals方法,并且该equals方法适用于子类。如AbstractList,AbstractMap等。

3) 无需判断逻辑相等。如java.util.Collections,只提供方法,不提供数据。

equals方法规约:

1) 自反性(reflexive):对于任何非null的引用值x,x.equals(x)必须返回true。

2) 对称性(symmetric):对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。

3) 传递性(transitive):对于任何非null的引用值x,y和z,如果x.equals(y)返回true,且y.equals(z)返回true,那么x.equals(z)也必须返回true。

4) 一致性(consistent):对于任何非null的引用值x和y,只有equals的比较操作在实例中所用的信息没有被修改,多次调用x.equals(y)总会一致地返回相同的结果。

5) 非空性(Non-nullity):对于任何非null的引用值x,x.equals(null)必须返回false。

JDK equals实现参考:

String.equals(Object) //比较实例->比较字符串长度->遍历char[]逐个比较

Date.equals(Object) //比较毫秒数

Arrays.equals(Object[],Object[]) //比较实例->比较数组长度->遍历数组逐个比较

2. Object.hashCode()

hashcode方法返回实例的哈希码,用于支持散列表如HashMap等。默认实现是将实例的内部地址转化为int值。

hash(散列)可以理解为让数据有规则的分散开来。为何要分散开?假设HashMap中很多Key的hashcode相同,这时map中会存在一个链表特别长,如果要查询map中哈希冲突的key对应的value,最坏的情况下要把这个特别长的链表全部遍历,这样会严重影响map的性能。如何你的hashcode方法设计的足够巧妙,使得map中的数据均匀的散列在各个链表中,那么这个map的性能是比较高的。

hashcode方法规约:

1) 同一实例上多次调用hashCode方法必须返回相同的int值。

2) 如果objectA.equals(objectB)为真,则objectA.hashcode()应该等于objectB.hashcode()。

3) 如果objectA.equals(objectB)为假,则不要求objectA.hashcode()等于objectB.hashcode()。但是为不同的实例生成不同的hashcode可提高HashMap等容器的性能。

常见数据类型hash算法:

如果需要覆盖普通类的equals与hashcode方法,可以使用IDE自带的代码生成工具:

idea: Code -> Generate.. -> equals and hashcode -> 选择关键字段

3. Object.clone()

创建并返回当前实例的副本。

clone方法规约:

1) 对于任意实例x,x.clone() != x

2) 对于任意实例x,x.clone().equals(x)

注意:Object.clone()是protected方法,要实现克隆必须重写该方法并实现Cloneable(空接口,仅作标识),否则会抛出CloneNotSupportedException。

4. Comparable.compareTo(T)

实现了Comparable接口的类的实例可互相比较大小,compareTo方法返回负整数、0、正整数对应于当前实例小于、等于、大于指定实例。

该方法用于数组或集合元素的排序,如:

java.util.Arrays.sort(Object[]) (该方法在排序时会将数组元素强制转换为Comparable,如果没有实现Comparable则抛出类型转换异常);

java.util.Collections

<T extends Comparable<? super T>> void sort(List<T> list)

compareTo方法规约:

1) x.compareTo(y) == y.compareTo(x),如果x.compareTo(y)抛出异常则y.compareTo(x)也应该抛出异常

2) 传递性:如果(x.compareTo(y)>0 && y.compareTo(z)>0)为真,则x.compareTo(z)>0也为真

3) 如果x.compareTo(y)==0 则x.compareTo(z) == y.compareTo(z)

4) 强烈推荐但非必须,x.compareTo(y)==0) == (x.equals(y))

与Comparable功能相似的接口有Comparator,区别是前者需要在类内部实现Comparable接口,而Comparator通常是作为函数式接口(@FunctionalInterface)使用。

5. Object.toString()

返回当前实例的String表示格式。

Object的toString方法返回格式为:className + '@'+ 十六进制hashcode,即:

getClass().getName() + '@' + Integer.toHexString(hashCode())

toString返回的String应该是简单明了、易于阅读的实例表示法。对于需要打印对象信息的类应当重写此方法。

标签: #java通用方法