Java HashMap source code parsing 1

Source: Internet
Author: User
Tags rehash concurrentmodificationexception

Java HashMap source code parsing 1

HashMap First, let's look at how the API documentation describes:

Implementation of the Map interface based on the hash table. This implementation provides all optional ing operations and allows the use of null values and null keys. (The HashMap class is roughly the same as that of Hashtable except for non-synchronous and allowed null .) This class does not guarantee the order of mappings, especially it does not guarantee that the order remains unchanged.

This implementation assumes that the hash function distributes elements appropriately between buckets and provides stable performance for basic operations (get and put. The time required to iterate the collection view is proportional to the "capacity" (number of buckets) of the HashMap instance and its size (number of key-value ing relationships. Therefore, if iteration performance is important, do not set the initial capacity too high (or set the loading factor too low ).

The HashMap instance has two parameters that affect its performance: initial capacity and load factor. Capacity is the number of buckets in the hash table. The initial capacity is only the capacity of the hash table at creation. A load factor is a scale in which a hash table can reach full capacity before its capacity increases automatically. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, You need to rehash the hash table (that is, rebuild the internal data structure ), in this way, the hash table will have approximately two times the number of buckets.

Generally, the default load factor (. 75) seeks a compromise between time and space costs. Although the loading factor is too high, it reduces the space overhead, but it also increases the query cost (this is reflected in most HashMap operations, including get and put operations ). When setting the initial capacity, you should take into account the number of entries and their loading factors required in the ing to minimize the number of rehash operations. If the initial capacity is greater than the maximum number of entries divided by the load factor, the rehash operation is not performed.

If many ing relationships need to be stored in the HashMap instance, compared with the on-demand automatic rehash operation to increase the table capacity, creating a ing with enough initial capacity will make the ing more effective.

Note that this implementation is not synchronous.If multiple threads access a hash ing at the same time, and at least one thread modifies the ing from the structure, it must maintain external synchronization. (Schema Modification refers to any operation to add or delete one or more mappings. Changing only the values associated with the key already included in the instance is not a schema modification .) This is generally done by synchronizing the objects that encapsulate the ing. If such an object does not exist, use the Collections. synchronizedMap method to "Wrap" The ing. It is best to complete this operation at creation to prevent unexpected non-synchronous access to the ing, as shown below:

Map m = Collections. synchronizedMap (new HashMap (...));

All the iterators returned by the "collection view method" of this class fail quickly: After the iterator is created, if the ing is modified from the structure, the iterator throws ConcurrentModificationException unless it is modified in any way at any time through the remove Method of the iterator itself. Therefore, in the face of concurrent modifications, the iterator will soon fail completely, without the risk of any uncertain behavior at an uncertain time in the future.

Note: The Fast failure behavior of the iterator cannot be guaranteed. In general, it is impossible to make any firm guarantee when there are non-synchronous concurrent modifications. The quick failure iterator tries its best to throw ConcurrentModificationException. Therefore, writing a program dependent on this exception is incorrect. The correct practice is that the fast failure behavior of the iterator should only be used to detect program errors.

 

 

From the source code, let's look at the HashMap constructor:

public HashMap() {        this.loadFactor = DEFAULT_LOAD_FACTOR;        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);        table = new Entry[DEFAULT_INITIAL_CAPACITY];        init();}

Analysis:

this.loadFactor = DEFAULT_LOAD_FACTOR;

The ult_load_factor is specified here. The ult_load_factor is defined in the source code:

 

static final float DEFAULT_LOAD_FACTOR = 0.75f;

 

Why is the load factor set to 0.75? The API documentation identifies the default load factor (. 75) as a compromise between time and space costs. I am not very familiar with the reason. Some explanations on the Internet are that when the loading factor is too large, the chance of conflict will increase. In an hour, it will waste space. In the Hash table, if we put a conflict, that is, two different objects, but with the same hashCode, occupy the same location in the Table. elements that conflict in the same location space are saved through a linked list, so when we get, if a conflict arises, we need to access the linked list, and frequent access to the linked list will cause high costs.

 

threshole=(int)(DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR);

Set the critical value to DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR.

DEFAULT_INITIAL_CAPACITY value:

static final int DEFAULT_INITIAL_CAPACITY=16

That is, the critical value is 12. When the actual size exceeds this value, the system will expand.

 

 

table = new Entry[DEFAULT_INITIAL_CAPACITY];

 

The object array storing elements, that is, HashMap, is an Entry array. The Entry object contains keys and values. Let's take a look at the Entry:

static class Entry
 
   implements Map.Entry
  
    {        final K key;        V value;        Entry
   
     next;        final int hash;        /**         * Creates new entry.         */        Entry(int h, K k, V v, Entry
    
      n) {            value = v;            next = n;            key = k;            hash = h;        }        public final K getKey() {            return key;        }        public final V getValue() {            return value;        }        public final V setValue(V newValue) {    V oldValue = value;            value = newValue;            return oldValue;        }        public final boolean equals(Object o) {            if (!(o instanceof Map.Entry))                return false;            Map.Entry e = (Map.Entry)o;            Object k1 = getKey();            Object k2 = e.getKey();            if (k1 == k2 || (k1 != null && k1.equals(k2))) {                Object v1 = getValue();                Object v2 = e.getValue();                if (v1 == v2 || (v1 != null && v1.equals(v2)))                    return true;            }            return false;        }        public final int hashCode() {            return (key==null   ? 0 : key.hashCode()) ^                   (value==null ? 0 : value.hashCode());        }        public final String toString() {            return getKey() + = + getValue();        }        /**         * This method is invoked whenever the value in an entry is         * overwritten by an invocation of put(k,v) for a key k that's already         * in the HashMap.         */        void recordAccess(HashMap
     
       m) {        }        /**         * This method is invoked whenever the entry is         * removed from the table.         */        void recordRemoval(HashMap
      
        m) { } }
      
     
    
   
  
 


Let's take a look at the Entry constructor:


Entry(int h, K k, V v, Entry
 
   n) {            value = v;            next = n;            key = k;            hash = h; }
 

H indicates the hash value, k indicates the key, v indicates the value, and n indicates the next node. From the above, we can also see that it implements functions such as getKey (), getValue (), setValue (V value), equals (Object o), and hashCode,

See the implementation of the equals method:


public final boolean equals(Object o) {            if (!(o instanceof Map.Entry))                return false;            Map.Entry e = (Map.Entry)o;            Object k1 = getKey();            Object k2 = e.getKey();            if (k1 == k2 || (k1 != null && k1.equals(k2))) {                Object v1 = getValue();                Object v2 = e.getValue();                if (v1 == v2 || (v1 != null && v1.equals(v2)))                    return true;            }            return false;        }


 

First, judge whether it is an instance of Map. Entry, not false, and then take out the keys and values of the two to judge. If the values are the same, true is returned; otherwise, false is returned.

 


 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.