HashMap(java7)java
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {} 數組
HashMap的數據結構是數組+鏈表,從上面的源碼能夠看出來,hashMap繼承了AbstractMap<K,V>的抽象類,實現了Map<K,V>的接口。安全
1、put方法:數據結構
Java源碼:app
1 /** 2 * Hash table based implementation of the <tt>Map</tt> interface. This 3 * implementation provides all of the optional map operations, and permits 4 * <tt>null</tt> values and the <tt>null</tt> key. (The <tt>HashMap</tt> 5 * class is roughly equivalent to <tt>Hashtable</tt>, except that it is 6 * unsynchronized and permits nulls.) This class makes no guarantees as to 7 * the order of the map; in particular, it does not guarantee that the order 8 * will remain constant over time. 9 * 10 * <p>This implementation provides constant-time performance for the basic 11 * operations (<tt>get</tt> and <tt>put</tt>), assuming the hash function 12 * disperses the elements properly among the buckets. Iteration over 13 * collection views requires time proportional to the "capacity" of the 14 * <tt>HashMap</tt> instance (the number of buckets) plus its size (the number 15 * of key-value mappings). Thus, it's very important not to set the initial 16 * capacity too high (or the load factor too low) if iteration performance is 17 * important. 18 * 19 * <p>An instance of <tt>HashMap</tt> has two parameters that affect its 20 * performance: <i>initial capacity</i> and <i>load factor</i>. The 21 * <i>capacity</i> is the number of buckets in the hash table, and the initial 22 * capacity is simply the capacity at the time the hash table is created. The 23 * <i>load factor</i> is a measure of how full the hash table is allowed to 24 * get before its capacity is automatically increased. When the number of 25 * entries in the hash table exceeds the product of the load factor and the 26 * current capacity, the hash table is <i>rehashed</i> (that is, internal data 27 * structures are rebuilt) so that the hash table has approximately twice the 28 * number of buckets. 29 * 30 * <p>As a general rule, the default load factor (.75) offers a good tradeoff 31 * between time and space costs. Higher values decrease the space overhead 32 * but increase the lookup cost (reflected in most of the operations of the 33 * <tt>HashMap</tt> class, including <tt>get</tt> and <tt>put</tt>). The 34 * expected number of entries in the map and its load factor should be taken 35 * into account when setting its initial capacity, so as to minimize the 36 * number of rehash operations. If the initial capacity is greater 37 * than the maximum number of entries divided by the load factor, no 38 * rehash operations will ever occur. 39 * 40 * <p>If many mappings are to be stored in a <tt>HashMap</tt> instance, 41 * creating it with a sufficiently large capacity will allow the mappings to 42 * be stored more efficiently than letting it perform automatic rehashing as 43 * needed to grow the table. 44 * 45 * <p><strong>Note that this implementation is not synchronized.</strong> 46 * If multiple threads access a hash map concurrently, and at least one of 47 * the threads modifies the map structurally, it <i>must</i> be 48 * synchronized externally. (A structural modification is any operation 49 * that adds or deletes one or more mappings; merely changing the value 50 * associated with a key that an instance already contains is not a 51 * structural modification.) This is typically accomplished by 52 * synchronizing on some object that naturally encapsulates the map. 53 */
而後咱們再看一下,HashMap的組成,主要的是實現方法爲put和get方法,主要的參數有capacity(桶的容量)和load factor(加載因子)。上面是官網的描述,大體意思爲:ide
一、HashMap實現了Map的接口,<K,V>對中的key和value均可覺得空,除了不是線程安全的和容許爲null外,幾乎是與HashTable同樣的。同時也不能保證其的順序。性能
二、一個hashmap對象主要有兩個參數capacity(桶的容量)和load factor(加載因子),默認load factor爲0.75是在時間和空間上性能最優的。測試
1 /** 2 * Constructs an empty <tt>HashMap</tt> with the default initial capacity 3 * (16) and the default load factor (0.75). 4 */ 5 public HashMap() { 6 this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); 7 }
上面的是不帶參數的HashMap的構造器,也是咱們建立對象的時候常常使用的,默認數組或者桶的容量爲16,加載因子爲0.75。ui
1 /** 2 * Constructs an empty <tt>HashMap</tt> with the specified initial 3 * capacity and the default load factor (0.75). 4 * 5 * @param initialCapacity the initial capacity. 6 * @throws IllegalArgumentException if the initial capacity is negative. 7 */ 8 public HashMap(int initialCapacity) { 9 this(initialCapacity, DEFAULT_LOAD_FACTOR); 10 }
咱們也能夠自定義數組的容量,加載因子默認爲0.75。this
/** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and load factor. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; threshold = initialCapacity; init(); }
同時也能夠既修改容量有修改加載因子,可是最好不要修改。
1 /** 2 * Associates the specified value with the specified key in this map. 3 * If the map previously contained a mapping for the key, the old 4 * value is replaced. 5 * 6 * @param key key with which the specified value is to be associated 7 * @param value value to be associated with the specified key 8 * @return the previous value associated with <tt>key</tt>, or 9 * <tt>null</tt> if there was no mapping for <tt>key</tt>. 10 * (A <tt>null</tt> return can also indicate that the map 11 * previously associated <tt>null</tt> with <tt>key</tt>.) 12 */ 13 public V put(K key, V value) { 14 if (table == EMPTY_TABLE) { 15 inflateTable(threshold); 16 } 17 if (key == null) 18 return putForNullKey(value); 19 int hash = hash(key); 20 int i = indexFor(hash, table.length); 21 for (Entry<K,V> e = table[i]; e != null; e = e.next) { 22 Object k; 23 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 24 V oldValue = e.value; 25 e.value = value; 26 e.recordAccess(this); 27 return oldValue; 28 } 29 } 30 31 modCount++;// 32 addEntry(hash, key, value, i); 33 return null; 34 }
1 /** 2 * Offloaded version of get() to look up null keys. Null keys map 3 * to index 0. This null case is split out into separate methods 4 * for the sake of performance in the two most commonly used 5 * operations (get and put), but incorporated with conditionals in 6 * others. 7 */ 8 private V getForNullKey() { 9 if (size == 0) { 10 return null; 11 } 12 for (Entry<K,V> e = table[0]; e != null; e = e.next) { 13 if (e.key == null) 14 return e.value; 15 } 16 return null; 17 }
1 /** 2 * Returns index for hash code h.返回hashCode值 3 */ 4 static int indexFor(int h, int length) { 5 // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; 6 return h & (length-1); 7 }
解析:首先判斷table,也就是數組是否爲空,爲空的話就去使用inflateTable的方法(這裏很少解釋)初始化hashmap。
若是table不爲空的話,就判斷key是否爲空,爲空的話就將放到數組的index=0的位置,若是value不爲空則返回value值。
若是key不爲空的話,就經過key獲取hash值,經過hash值和table的長度與運算獲取hashCode值。
經過hashCode的遍歷entry<K,V>的鍵值對,若是key的hash值相等 而且key.equals(e.key)也相等的話,就將新的value替換掉舊的,返回舊值。
在put的過程當中modCount記錄修改的次數,用於fail-fast容錯。
那麼會有人問了,爲何table.length的默認長度爲16,而不是15或者14呢?
首先,經過計算你就會發現hash取模使用&比使用%,獲得的hash會更均勻,而且性能更好。例如:table.length=15
h&(length-1) hash length - 1 結果
8 & (15-1) 1000 1110 1000
9 & (15-1) 1001 1110 1000
-------------------------------------------------------------------------------------------------------------------------------------------------------
8 & (16-1) 1000 1111 1000
9 & (16-1) 1001 1111 1001
從上面的結果你能夠發現,當table.length=15的時候,會出現hash碰撞的現象,而且全部結果爲最後一位1的永遠都不會出現,既0001,1001,1011,0011
0101,1111這幾個位置都不會存放數據,會出現存放不均勻,而且浪費資源的現象。若是將8和9放到同一個位置當咱們get的時候,要遍歷鏈表,下降了查 詢效率。
1 /** 2 * Adds a new entry with the specified key, value and hash code to 3 * the specified bucket. It is the responsibility of this 4 * method to resize the table if appropriate. 5 * 6 * Subclass overrides this to alter the behavior of put method. 7 */ 8 void addEntry(int hash, K key, V value, int bucketIndex) { 9 if ((size >= threshold) && (null != table[bucketIndex])) { 10 resize(2 * table.length); 11 hash = (null != key) ? hash(key) : 0; 12 bucketIndex = indexFor(hash, table.length); 13 } 14 15 createEntry(hash, key, value, bucketIndex); 16 } 17 18 /** 19 * Like addEntry except that this version is used when creating entries 20 * as part of Map construction or "pseudo-construction" (cloning, 21 * deserialization). This version needn't worry about resizing the table. 22 * 23 * Subclass overrides this to alter the behavior of HashMap(Map), 24 * clone, and readObject. 25 */ 26 void createEntry(int hash, K key, V value, int bucketIndex) { 27 Entry<K,V> e = table[bucketIndex]; 28 table[bucketIndex] = new Entry<>(hash, key, value, e); 29 size++; 30 }
當咱們在addEntry的時候,新的添加的entry都會放到第一個位置,而且指向下一個entry,每個entry中存放一個key-value和next引用。
2、get方法:
Java源碼:
1 /** 2 * Returns the value to which the specified key is mapped, 3 * or {@code null} if this map contains no mapping for the key. 4 * 5 * <p>More formally, if this map contains a mapping from a key 6 * {@code k} to a value {@code v} such that {@code (key==null ? k==null : 7 * key.equals(k))}, then this method returns {@code v}; otherwise 8 * it returns {@code null}. (There can be at most one such mapping.) 9 * 10 * <p>A return value of {@code null} does not <i>necessarily</i> 11 * indicate that the map contains no mapping for the key; it's also 12 * possible that the map explicitly maps the key to {@code null}. 13 * The {@link #containsKey containsKey} operation may be used to 14 * distinguish these two cases. 15 * 16 * @see #put(Object, Object) 17 */ 18 public V get(Object key) { 19 if (key == null) 20 return getForNullKey(); 21 Entry<K,V> entry = getEntry(key); 22 23 return null == entry ? null : entry.getValue(); 24 } 25 26 /** 27 * Offloaded version of get() to look up null keys. Null keys map 28 * to index 0. This null case is split out into separate methods 29 * for the sake of performance in the two most commonly used 30 * operations (get and put), but incorporated with conditionals in 31 * others. 32 */ 33 private V getForNullKey() { 34 if (size == 0) { 35 return null; 36 } 37 for (Entry<K,V> e = table[0]; e != null; e = e.next) { 38 if (e.key == null) 39 return e.value; 40 } 41 return null; 42 }
1 /** 2 * Returns the entry associated with the specified key in the 3 * HashMap. Returns null if the HashMap contains no mapping 4 * for the key. 5 */ 6 final Entry<K,V> getEntry(Object key) { 7 if (size == 0) { 8 return null; 9 } 10 11 int hash = (key == null) ? 0 : hash(key); 12 for (Entry<K,V> e = table[indexFor(hash, table.length)]; 13 e != null; 14 e = e.next) { 15 Object k; 16 if (e.hash == hash && 17 ((k = e.key) == key || (key != null && key.equals(k)))) 18 return e; 19 } 20 return null; 21 }
解析:
(1)get的時候,若是key==null,判斷map的長度也爲空的話,則返回null,若是map長度不爲空,則e也不空,遍歷table[0],返回e.value
(2)getEntry的時候,首先要獲取hash(key)的值,經過hash&table.length獲取到的hashCode值獲得entry在桶中存放的位置,判斷若是若是傳入的key的hash與要得到key的hash相等的話而且key.equals(e.key)y也相等,則返回entry,若是返回的jentry不爲空的話,則getValue的值。
3、map中的元素不重複,元素無序
測試:
1 Map map = new HashMap(); 2 map.put("123","456"); 3 map.put("abc","456"); 4 map.put("dd","456"); 5 map.put("ss","789"); 6 Object s = map.put("123","777"); 7 System.out.println(map);//key不可重複,重複的話會將原來的value值覆蓋,可是value的值不是惟一的,每一個key都有惟一的一個value與之對應 8 System.out.println(s);//返回舊的value值456
若有錯誤,請多多指出,謝謝!