标签:
【Java心得总结七】Java容器下——Map 在自己总结的这篇文章中有提到hashCode,但是没有细究,今天细究整理一下hashCode相关问题
首先我们都知道hashCode()和equals()函数是java基类Object的一部分,我查阅了java7文档,其中对于两者的描述如下:
解读这里对hashCode的描述,不难发现:
这里突然发现《java编程思想》中对于equals的描述原来出自这里:
Java中HashMap的实现,我截取了部分代码如下:
代码段-1
1 /* HashMap实现部分代码 */ 2 public class HashMap<K,V> 3 extends AbstractMap<K,V> 4 implements Map<K,V>, Cloneable, Serializable 5 { 6 /** 7 * The default initial capacity - MUST be a power of two. 8 */ 9 static final int DEFAULT_INITIAL_CAPACITY = 16; 10 11 /** 12 * The maximum capacity, used if a higher value is implicitly specified 13 * by either of the constructors with arguments. 14 * MUST be a power of two <= 1<<30. 15 */ 16 static final int MAXIMUM_CAPACITY = 1 << 30; 17 18 /** 19 * The load factor used when none specified in constructor. 20 */ 21 static final float DEFAULT_LOAD_FACTOR = 0.75f; 22 23 /** 24 * The table, resized as necessary. Length MUST Always be a power of two. 25 */ 26 transient Entry<K,V>[] table; 27 28 /** 29 * The number of key-value mappings contained in this map. 30 */ 31 transient int size; 32 33 /** 34 * The next size value at which to resize (capacity * load factor). 35 * @serial 36 */ 37 int threshold; 38 39 /** 40 * The load factor for the hash table. 41 * 42 * @serial 43 */ 44 final float loadFactor; 45 46 /** 47 * Retrieve object hash code and applies a supplemental hash function to the 48 * result hash, which defends against poor quality hash functions. This is 49 * critical because HashMap uses power-of-two length hash tables, that 50 * otherwise encounter collisions for hashCodes that do not differ 51 * in lower bits. Note: Null keys always map to hash 0, thus index 0. 52 */ 53 final int hash(Object k) { 54 int h = 0; 55 if (useAltHashing) { 56 if (k instanceof String) { 57 return sun.misc.Hashing.stringHash32((String) k); 58 } 59 h = hashSeed; 60 } 61 62 h ^= k.hashCode(); 63 64 // This function ensures that hashCodes that differ only by 65 // constant multiples at each bit position have a bounded 66 // number of collisions (approximately 8 at default load factor). 67 h ^= (h >>> 20) ^ (h >>> 12); 68 return h ^ (h >>> 7) ^ (h >>> 4); 69 } 70 71 /** 72 * Returns index for hash code h. 73 */ 74 static int indexFor(int h, int length) { 75 return h & (length-1); 76 } 77 78 /** 79 * Adds a new entry with the specified key, value and hash code to 80 * the specified bucket. It is the responsibility of this 81 * method to resize the table if appropriate. 82 * 83 * Subclass overrides this to alter the behavior of put method. 84 */ 85 void addEntry(int hash, K key, V value, int bucketIndex) { 86 if ((size >= threshold) && (null != table[bucketIndex])) { 87 resize(2 * table.length); 88 hash = (null != key) ? hash(key) : 0; 89 bucketIndex = indexFor(hash, table.length); 90 } 91 92 createEntry(hash, key, value, bucketIndex); 93 } 94 95 /** 96 * Like addEntry except that this version is used when creating entries 97 * as part of Map construction or "pseudo-construction" (cloning, 98 * deserialization). This version needn‘t worry about resizing the table. 99 * 100 * Subclass overrides this to alter the behavior of HashMap(Map), 101 * clone, and readObject. 102 */ 103 void createEntry(int hash, K key, V value, int bucketIndex) { 104 Entry<K,V> e = table[bucketIndex]; 105 table[bucketIndex] = new Entry<>(hash, key, value, e); 106 size++; 107 } 108 109 /** 110 * Associates the specified value with the specified key in this map. 111 * If the map previously contained a mapping for the key, the old 112 * value is replaced. 113 * 114 * @param key key with which the specified value is to be associated 115 * @param value value to be associated with the specified key 116 * @return the previous value associated with <tt>key</tt>, or 117 * <tt>null</tt> if there was no mapping for <tt>key</tt>. 118 * (A <tt>null</tt> return can also indicate that the map 119 * previously associated <tt>null</tt> with <tt>key</tt>.) 120 */ 121 public V put(K key, V value) { 122 if (key == null) 123 return putForNullKey(value); 124 int hash = hash(key); 125 int i = indexFor(hash, table.length); 126 for (Entry<K,V> e = table[i]; e != null; e = e.next) { 127 Object k; 128 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 129 V oldValue = e.value; 130 e.value = value; 131 e.recordAccess(this); 132 return oldValue; 133 } 134 } 135 136 modCount++; 137 addEntry(hash, key, value, i); 138 return null; 139 } 140 141 /** 142 * Returns the entry associated with the specified key in the 143 * HashMap. Returns null if the HashMap contains no mapping 144 * for the key. 145 */ 146 final Entry<K,V> getEntry(Object key) { 147 int hash = (key == null) ? 0 : hash(key); 148 for (Entry<K,V> e = table[indexFor(hash, table.length)]; 149 e != null; 150 e = e.next) { 151 Object k; 152 if (e.hash == hash && 153 ((k = e.key) == key || (key != null && key.equals(k)))) 154 return e; 155 } 156 return null; 157 } 158 159 /** 160 * Removes and returns the entry associated with the specified key 161 * in the HashMap. Returns null if the HashMap contains no mapping 162 * for this key. 163 */ 164 final Entry<K,V> removeEntryForKey(Object key) { 165 int hash = (key == null) ? 0 : hash(key); 166 int i = indexFor(hash, table.length); 167 Entry<K,V> prev = table[i]; 168 Entry<K,V> e = prev; 169 170 while (e != null) { 171 Entry<K,V> next = e.next; 172 Object k; 173 if (e.hash == hash && 174 ((k = e.key) == key || (key != null && key.equals(k)))) { 175 modCount++; 176 size--; 177 if (prev == e) 178 table[i] = next; 179 else 180 prev.next = next; 181 e.recordRemoval(this); 182 return e; 183 } 184 prev = e; 185 e = next; 186 } 187 188 return e; 189 } 190 191 /** 192 * Rehashes the contents of this map into a new array with a 193 * larger capacity. This method is called automatically when the 194 * number of keys in this map reaches its threshold. 195 * 196 * If current capacity is MAXIMUM_CAPACITY, this method does not 197 * resize the map, but sets threshold to Integer.MAX_VALUE. 198 * This has the effect of preventing future calls. 199 * 200 * @param newCapacity the new capacity, MUST be a power of two; 201 * must be greater than current capacity unless current 202 * capacity is MAXIMUM_CAPACITY (in which case value 203 * is irrelevant). 204 */ 205 void resize(int newCapacity) { 206 Entry[] oldTable = table; 207 int oldCapacity = oldTable.length; 208 if (oldCapacity == MAXIMUM_CAPACITY) { 209 threshold = Integer.MAX_VALUE; 210 return; 211 } 212 213 Entry[] newTable = new Entry[newCapacity]; 214 boolean oldAltHashing = useAltHashing; 215 useAltHashing |= sun.misc.VM.isBooted() && 216 (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD); 217 boolean rehash = oldAltHashing ^ useAltHashing; 218 transfer(newTable, rehash); 219 table = newTable; 220 threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); 221 } 222 223 /** 224 * Transfers all entries from current table to newTable. 225 */ 226 void transfer(Entry[] newTable, boolean rehash) { 227 int newCapacity = newTable.length; 228 for (Entry<K,V> e : table) { 229 while(null != e) { 230 Entry<K,V> next = e.next; 231 if (rehash) { 232 e.hash = null == e.key ? 0 : hash(e.key); 233 } 234 int i = indexFor(e.hash, newCapacity); 235 e.next = newTable[i]; 236 newTable[i] = e; 237 e = next; 238 } 239 } 240 } 241 }
代码段-2
1 static class Entry<K,V> implements Map.Entry<K,V> { 2 final K key; 3 V value; 4 Entry<K,V> next; 5 int hash; 6 7 /** 8 * Creates new entry. 9 */ 10 Entry(int h, K k, V v, Entry<K,V> n) { 11 value = v; 12 next = n; 13 key = k; 14 hash = h; 15 } 16 }
我将HahsMap中的增删改查以及相关用到的函数截取了出来以作分析:
通过上面的分析我们也会发现如何构造一个优良的散列函数是一件非常重要的事情,我们构造散列函数的基本原则就是:尽可能的减少冲突,尽可能的将元素“散列”在存储空间中
下面是我从维基上找到的一些方法,之后如果有好的想法再做补充:
而在反观Java中的散列函数:
代码段-3
1 /** 2 * A randomizing value associated with this instance that is applied to 3 * hash code of keys to make hash collisions harder to find. 4 */ 5 transient final int hashSeed = sun.misc.Hashing.randomHashSeed(this); 6 7 /** 8 * Retrieve object hash code and applies a supplemental hash function to the 9 * result hash, which defends against poor quality hash functions. This is 10 * critical because HashMap uses power-of-two length hash tables, that 11 * otherwise encounter collisions for hashCodes that do not differ 12 * in lower bits. Note: Null keys always map to hash 0, thus index 0. 13 */ 14 final int hash(Object k) { 15 int h = 0; 16 if (useAltHashing) { 17 if (k instanceof String) { 18 return sun.misc.Hashing.stringHash32((String) k); 19 } 20 h = hashSeed; 21 } 22 23 h ^= k.hashCode(); 24 25 // This function ensures that hashCodes that differ only by 26 // constant multiples at each bit position have a bounded 27 // number of collisions (approximately 8 at default load factor). 28 h ^= (h >>> 20) ^ (h >>> 12); 29 return h ^ (h >>> 7) ^ (h >>> 4); 30 }
似乎Java是综合运用了上面几种方法来计算哈希值
上面有些地方是自己的一些理解,如果碰巧某位仁兄看到那里说的不对了还请指正~
标签:
原文地址:http://www.cnblogs.com/xltcjylove/p/4445574.html