equals() 与hasCode() 都定义在Object.java中。equals 默认使用“==”来比较对象。java
public boolean equals(Object obj) { return (this == obj); } public native int hashCode();
在对于hashCode()的javadoc中明确代表: hashCode是用在hash表中的。 equals相同,hashCode必定相同(但hashCode相同,equals不必定相同),这样有利于提升hash表的效率。算法
eg. 验证: hashCode相同,equals不必定相同源码分析
public static void main(String[] args) { Long a = 1L; Integer b = 1; System.out.println(b.hashCode() == a.hashCode()); // true System.out.println(b.equals(a)); // false }
执行结果: true、 false。ui
对于String: this
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; } public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
对于Boolean:
hashCode() 为定值: true->1231! fale -> 1237。 equals() 先断定是不是本类的实例再比较值。spa
public int hashCode() { return value ? 1231 : 1237; } public boolean equals(Object obj) { if (obj instanceof Boolean) { return value == ((Boolean)obj).booleanValue(); } return false; }
对于比Integer范围小的short、byte、char :
hashCode() 直接强转int。 equals() 先断定是不是本类的实例再比较值。.net
public int hashCode() { return (int)value; } public boolean equals(Object obj) { if (obj instanceof Short) { return value == ((Short)obj).shortValue(); } return false; }
对于比Integer范围大的Long、float、double:
hashCode(): 则须要处理转换成int。double比较特殊些:是先处理为long,再用long取hashCode()的方式转int;
equals(): Long是直接比较数值; 而float、double是要转成int进行比较(按javadoc中所述 须要考虑NaN的问题),NaN直接返回定值。code
public static void main(String[] args) { float a = Float.NaN; Float b = Float.NaN; System.out.println(a == b); // flase System.out.println(b.equals(b)); // true float a2 = 1f; Float b2 = 1f; System.out.println(a2 == b2); // true System.out.println(b2.equals(b2)); // true }
执行结果: false、true、true、true。 对象
NaN与任何浮点数(包括自身)的比较结果都为假,这是NaN独有的特性,因此可使用与本身相比来肯定当前数值是否是一个正常的数.blog
结论: 若是仅仅考虑非NaN的问题能够直接用floatValue()、doubleValue()方法。但由于float与double有NaN因此须要考虑!
源码以下:
Long: public static int hashCode(long value) { return (int)(value ^ (value >>> 32)); } public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; } Float: /** * A constant holding a Not-a-Number (NaN) value of type * {@code float}. It is equivalent to the value returned by * {@code Float.intBitsToFloat(0x7fc00000)}. */ public static final float NaN = 0.0f / 0.0f; public static int hashCode(float value) { return floatToIntBits(value); } public boolean equals(Object obj) { return (obj instanceof Float) && (floatToIntBits(((Float)obj).value) == floatToIntBits(value)); } public static int floatToIntBits(float value) { int result = floatToRawIntBits(value); // Check for NaN based on values of bit fields, maximum // exponent and nonzero significand. if ( ((result & FloatConsts.EXP_BIT_MASK) == FloatConsts.EXP_BIT_MASK) && (result & FloatConsts.SIGNIF_BIT_MASK) != 0) result = 0x7fc00000; return result; } Double: public int hashCode() { long bits = doubleToLongBits(value); return (int)(bits ^ (bits >>> 32)); } public static long doubleToLongBits(double value) { long result = doubleToRawLongBits(value); // Check for NaN based on values of bit fields, maximum // exponent and nonzero significand. if ( ((result & DoubleConsts.EXP_BIT_MASK) == DoubleConsts.EXP_BIT_MASK) && (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L) result = 0x7ff8000000000000L; return result; } /** * A constant holding a Not-a-Number (NaN) value of type * {@code double}. It is equivalent to the value returned by * {@code Double.longBitsToDouble(0x7ff8000000000000L)}. */ public static final double NaN = 0.0d / 0.0;
维护 hashCode 方法的常规协定,该协定声明相等对象必须具备相等的哈希码。
hashcode是用于散列数据的快速存取,如利用HashSet、HashMap、Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。
hashSet的核心就是经过HashMap的key来实现的
/** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap<>(); } public boolean add(E e) { return map.put(e, PRESENT)==null; }
经过hashMap的源码可见: hash(Object key)对于定位对象在table的位置相当重要,其中就会使用对象的hashCode值。
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
因此在老版本(1.7)hashMap的实现中对于hash定位相同的值将使用拉链法存储:存储table位置上的是一个Map.Entry对象链表。新值对若是hash定位相同,equals不一样,则封装成Map.Entry对象将插入进该链表的头部,next指向链表原头部;该链表查找算法的时间复杂度为0(n)。而在1.8改成当链表长度超过TREEIFY_THRESHOLD(默认 8) <实则是9>则转为红黑树存储,因红黑树的特性,查找算法的时间复杂度为O(log2 n)。(hashTable的处理仍是拉链法)