之前 , 听过很多次,重写equals()方法的时候,必须重写hashcode() ,
两个对象equals,hashcode()必然相等,两个对象hash值相等,当时不一定equals;
那么 究竟hashcode 是什么?起的作用又是什么?
hashcode() 和 equals() 方法都是Object(终极父类)中的方法;
如果子类 没有重写 , 那么就默认的调用父类中的方法;
Object 中的hashcode方法是通过本地方法实现的,并不是通过Java语言
public native int hashCode(); //This is typically implemented by converting the internal address of the object into an integer //通过把对象的地址空间转换成一个整数实现的
String 中的hashcode() :
public int hashCode() { int h = hash; // value 是 字符数组,字符串是一个字符数组构成的 if (h == 0 && value.length > 0) { char val[] = value; //散列公式: s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
hashcode的作用就体现在构建hashMap , hashtable 这种集合的时候;
它们首先会根据对象hash值,找到相对应的数组下标,进行比较 , 插入和读取;
如下面的hashmap中的get(Object key) 方法:
public V get(Object key) { if (key == null) return getForNullKey(); //hashmap通过拉链法处理冲突的,Entry是链表节点类型 Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue(); }
final Entry<K,V> getEntry(Object key) { if (size == 0) { return null; } // 看,这一步获取该key的hash值,hash(key)方法就调用key的hashcode方法 //所以,为什么我们自定义的类型做key都要重写hashcode方法,下面会告诉你 //为什么要重写equals()方法 int hash = (key == null) ? 0 : hash(key); // 根据hash值找到index , 然后就是遍历链表 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; //这里不就说明为什么要重写equals吗 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; }
final int hash(Object k) { int h = hashSeed; if (0 != h && k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); } //这里不就是根据key的hashcode来生成相应的hash值 h ^= k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
关于HashMap的扩容?
void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } //新建一个新数组将容量扩大两倍 Entry[] newTable = new Entry[newCapacity]; // 接下来就是怎样把原来数组的中的元素放入新数组中! transfer(newTable, initHashSeedAsNeeded(newCapacity)); table = newTable; threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); } /** * Transfers all entries from current table to newTable. */ void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; //对链表数组中的每个元素 for (Entry<K,V> e : table) { //对链表中的每个元素 while(null != e) { Entry<K,V> next = e.next; if (rehash) { //对key可能要再次hash!!! e.hash = null == e.key ? 0 : hash(e.key); } //根据e的hash值与数组的容量求的索引的位置! int i = indexFor(e.hash, newCapacity); // 这一步的意思就是 前面可能有元素在i位置了,那么就把i位置的元素加在他后面! e.next = newTable[i]; newTable[i] = e; e = next; } } }