重载和重写
equals和==的区别
- ==主要用来比较基本数据类型,而equal主要用来比较对象是否相等。equal是Object的方法。
- 如果两者都用来比较对象的相等性,那么如果两个引用地址相同,那么==就返回true,但是如果对象重写了equal方法,那么就需要根据实际情况进行分析。比如String对象,根据字符串内容是否相等来返回true还是false。比如Integer,根据数值是否相等来返回true还是false。
hashCode
hashCode()是Obejct类的中一个方法,返回值是int。
hashCOde()就是根据一定的规则将对象的信息(对象的字段、对象的地址)散列成一个散列值。
hahCode的作用
hashCode是为了查找的快捷性。主要用于HashSet、HashMap、HashTable等散列集合。
hashCode()返回值不一定是对象的内存地址,只能说有些时候确实返回的值是内存地址但是大部分时候不是。(针对Object类)
如何在散列集合存放不同的对象,判断集合中是否存在这个对象。
- 用equals来一一比对,但是这样子如果集合越大所花费的资源也越大。
- 这个时候使用对象的hashCode方法的话,集合中会存在一个hash表。
- 先判断是否对象的hashCOde的值已经存在与这个hash表中,如果不存在就直接放入集合中,
- 如果存在,再使用equal比对hashCOde值相同的对象,如果equal比对true则不能放入集合,如果比对false则放入集合。这样就大大减少了执行equal方法的次数。
hashCode()与equals()的关系
先通过hashcode来比较,如果hashcode相等,那么就用equals方法来比较两个对象是否相等,用个例子说明:上面说的hash表中的8个位置,就好比8个桶,每个桶里能装很多的对象,对象A通过hash函数算法得到将它放到1号桶中,当然肯定有别的对象也会放到1号桶中,如果对象B也通过算法分到了1号桶,那么它如何识别桶中其他对象是否和它一样呢,这时候就需要equals方法来进行筛选了。
- 如果两个对象的equals相等,那么其hashCode值必相等。
- 如果两个对象的hashCOde值相等,其equals不一定相等。
equals()方法重写时hashCode()也必须要重写
让equals和hashCode在逻辑上始终保持一致性。不然一个对象重写了equals方法却没有重写hashCode方法后,两个没有关系的对象equals相等,但是hashCode不相等。
hashCode重写的规范
hash算法尽量不要依赖于对象中易变的数据,否则你存进去时散列值和你取出来的散列值可能不一样导致取不出来数据。
如果Object没有重写hashCode方法那么hashCode()是如何算出来?
通过下面代码可以看出hashCode值有时是靠对象内存地址算出来的,但具体还是取决与JVM的具体实现。
// hashCode() generation :
//
// Possibilities:
// * MD5Digest of {obj,stwRandom}
// * CRC32 of {obj,stwRandom} or any linear-feedback shift register function.
// * A DES- or AES-style SBox[] mechanism
// * One of the Phi-based schemes, such as:
// 2654435761 = 2^32 * Phi (golden ratio)
// HashCodeValue = ((uintptr_t(obj) >> 3) * 2654435761) ^ GVars.stwRandom ;
// * A variation of Marsaglia\'s shift-xor RNG scheme.
// * (obj ^ stwRandom) is appealing, but can result
// in undesirable regularity in the hashCode values of adjacent objects
// (objects allocated back-to-back, in particular). This could potentially
// result in hashtable collisions and reduced hashtable efficiency.
// There are simple ways to "diffuse" the middle address bits over the
// generated hashCode values:
//
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it\'s possible for two threads to race and generate the same RNG.
// On MP system we\'ll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ; // 随机数
} else
if (hashCode == 1) {
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
// 地址基础上hack
intptr_t addrBits = intptr_t(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
} else
if (hashCode == 2) {
value = 1 ; // for sensitivity testing, 实际不会使用
} else
if (hashCode == 3) {
value = ++GVars.hcSequence ;
} else
if (hashCode == 4) {
value = intptr_t(obj) ; // 直接用地址
} else {
// Marsaglia\'s xor-shift scheme with thread-specific state
// This is probably the best overall implementation -- we\'ll
// likely make this the default in future releases.
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
}
value &= markOopDesc::hash_mask;
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc::no_hash, "invariant") ;
TEVENT (hashCode: GENERATE) ;
return value;
}