1. 程式人生 > >Java原始碼解析HashMap簡介

Java原始碼解析HashMap簡介

本文基於jdk1.8進行分析。

HashMap是java開發中可以說必然會用到的一個集合。本文就HashMap的原始碼實現進行分析。

首先看一下原始碼中類的javadoc註釋對HashMap的解釋。如下圖。HashMap是對Map介面的基於hash表的實現。這個實現提供了map的所有可選操作,並且允許null值(可以多個)和一個null的key(僅限一個)。HashMap和HashTable十分相似,除了HashMap是非同步的且允許null元素。這個類不保證map裡的順序,更進一步,隨著時間的推移,它甚至不保證順序一直不變。

這個實現為get和put這樣的基本操作提供常量級效能,它假設hash函式把元素們比較好的分散到各個桶裡。用迭代器遍歷集合需要的時間,和HashMap的容量與HashMap裡的Entry數量的和成正比。所以,如果遍歷效能很重要的話,一定不要把初始容量設定的太大,或者把負載因子設定的太小。

一個hashmap有兩個影響它的效能的引數,初始容量和負載因子。容量是雜湊表中桶的數量,初始容量就是建立雜湊表時桶的數量。負載銀子是雜湊表的容量自動擴容前雜湊表能夠達到多滿。當雜湊表中條目的數量超過當前容量和負載因子的乘積後,雜湊表會進行重新雜湊(也就是,內部資料結構重建),以使雜湊表大約擁有2倍數量的桶。

作為一個通常的規則,預設負載銀子(0.75) 提供了一個時間和空間的比較好的平衡。更高的負載因子會降低空間消耗但是會增加查詢的消耗。當設定初始容量時,雜湊表中期望的條目數量和它的負載因子應該考慮在內,以儘可能的減小重新雜湊的次數。如果初始容量比條目最大數量除以負載因子還大,那麼重新雜湊操作就不會發生。

如果許多entry需要儲存在雜湊表中,用能夠容納entry的足夠大的容量來建立雜湊表,比讓它在需要的時候自動擴容更有效率。請注意,使用多個hash值相等的key肯定會降低任何雜湊表的效率。

請注意這個實現不是同步的。如果多個執行緒同時訪問雜湊表,並且至少有一個執行緒會修改雜湊表的結構,那麼雜湊表外部必須進行同步。

/**
 * Hash table based implementation of the <tt>Map</tt> interface.  This
 * implementation provides all of the optional map operations, and permits
 * <tt>null</tt> values and the <tt>null</tt> key.  (The <tt>HashMap</tt>
 * class is roughly equivalent to <tt>Hashtable</tt>, except that it is
 * unsynchronized and permits nulls.)  This class makes no guarantees as to
 * the order of the map; in particular, it does not guarantee that the order
 * will remain constant over time.
 *
 * <p>This implementation provides constant-time performance for the basic
 * operations (<tt>get</tt> and <tt>put</tt>), assuming the hash function
 * disperses the elements properly among the buckets.  Iteration over
 * collection views requires time proportional to the "capacity" of the
 * <tt>HashMap</tt> instance (the number of buckets) plus its size (the number
 * of key-value mappings).  Thus, it's very important not to set the initial
 * capacity too high (or the load factor too low) if iteration performance is
 * important.
 *
 * <p>An instance of <tt>HashMap</tt> has two parameters that affect its
 * performance: <i>initial capacity</i> and <i>load factor</i>.  The
 * <i>capacity</i> is the number of buckets in the hash table, and the initial
 * capacity is simply the capacity at the time the hash table is created.  The
 * <i>load factor</i> is a measure of how full the hash table is allowed to
 * get before its capacity is automatically increased.  When the number of
 * entries in the hash table exceeds the product of the load factor and the
 * current capacity, the hash table is <i>rehashed</i> (that is, internal data
 * structures are rebuilt) so that the hash table has approximately twice the
 * number of buckets.
 *
 * <p>As a general rule, the default load factor (.75) offers a good
 * tradeoff between time and space costs.  Higher values decrease the
 * space overhead but increase the lookup cost (reflected in most of
 * the operations of the <tt>HashMap</tt> class, including
 * <tt>get</tt> and <tt>put</tt>).  The expected number of entries in
 * the map and its load factor should be taken into account when
 * setting its initial capacity, so as to minimize the number of
 * rehash operations.  If the initial capacity is greater than the
 * maximum number of entries divided by the load factor, no rehash
 * operations will ever occur.
 *
 * <p>If many mappings are to be stored in a <tt>HashMap</tt>
 * instance, creating it with a sufficiently large capacity will allow
 * the mappings to be stored more efficiently than letting it perform
 * automatic rehashing as needed to grow the table.  Note that using
 * many keys with the same {@code hashCode()} is a sure way to slow
 * down performance of any hash table. To ameliorate impact, when keys
 * are {@link Comparable}, this class may use comparison order among
 * keys to help break ties.
 *
 * <p><strong>Note that this implementation is not synchronized.</strong>
 * If multiple threads access a hash map concurrently, and at least one of
 * the threads modifies the map structurally, it <i>must</i> be
 * synchronized externally.  (A structural modification is any operation
 * that adds or deletes one or more mappings; merely changing the value
 * associated with a key that an instance already contains is not a
 * structural modification.)  This is typically accomplished by
 * synchronizing on some object that naturally encapsulates the map.
 *
 * If no such object exists, the map should be "wrapped" using the
 * {@link Collections#synchronizedMap Collections.synchronizedMap}
 * method.  This is best done at creation time, to prevent accidental
 * unsynchronized access to the map:<pre>
 *   Map m = Collections.synchronizedMap(new HashMap(...));</pre>
 *
 * <p>The iterators returned by all of this class's "collection view methods"
 * are <i>fail-fast</i>: if the map is structurally modified at any time after
 * the iterator is created, in any way except through the iterator's own
 * <tt>remove</tt> method, the iterator will throw a
 * {@link ConcurrentModificationException}.  Thus, in the face of concurrent
 * modification, the iterator fails quickly and cleanly, rather than risking
 * arbitrary, non-deterministic behavior at an undetermined time in the
 * future.
 *
 * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
 * as it is, generally speaking, impossible to make any hard guarantees in the
 * presence of unsynchronized concurrent modification.  Fail-fast iterators
 * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
 * Therefore, it would be wrong to write a program that depended on this
 * exception for its correctness: <i>the fail-fast behavior of iterators
 * should be used only to detect bugs.</i>
 *
 * <p>This class is a member of the
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @param <K> the type of keys maintained by this map
 * @param <V> the type of mapped values
 *
 * @author  Doug Lea
 * @author  Josh Bloch
 * @author  Arthur van Hoff
 * @author  Neal Gafter
 * @see     Object#hashCode()
 * @see     Collection
 * @see     Map
 * @see     TreeMap
 * @see     Hashtable
 * @since   1.2
 */

This is the end。