Java Collection Learning (13) Weakhashmap Detailed introduction (source analysis) and usage examples

Source: Internet
Author: User
Tags abstract constructor empty final hash int size static class concurrentmodificationexception

In this chapter, we learn about Weakhashmap.
We first have a general understanding of Weakhashmap, and then learn its source, and finally through the example to learn to use Weakhashmap.

The 1th part Weakhashmap introduction

Weakhashmap Introduction

Weakhashmap inherits from Abstractmap and implements the map interface.
Like HashMap, Weakhashmap is also a hash table, which stores content that is also a key-value pair (key-value) mapping, and the keys and values can be null.
But Weakhashmap's key is "weak key". In Weakhashmap, when a key is no longer in normal use, it is automatically removed from the Weakhashmap. More precisely, for a given key, the presence of the mapping does not prevent the garbage collector from discarding the key, which makes the key available to terminate and then be recycled. When a key is terminated, its corresponding key-value pair is removed from the map effectively.
What is the principle of this "weak key"? In general, it is achieved through WeakReference and Referencequeue. Weakhashmap key is a "weak key", that is, a weakreference type; Referencequeue is a queue that holds "weak keys" that are recycled by GC. The implementation steps are:
(01) Create a new Weakhashmap and add "key value pairs" to the weakhashmap.
In fact, Weakhashmap saves entry (key-value pairs) by array table, and each entry is actually a one-way list, that is, entry is a key-value pair linked list.
(02) When a "weak key" is no longer referenced by other objects, and is collected by GC. When the GC reclaims the weak key, the weak key is also added to the Referencequeue (queue) queue.
(03) The next time we need to operate the WEAKHASHMAP, we will synchronize the table and queue first. All of the key-value pairs are saved in the table, and the queue is saved by GC-collected key-value pairs, which are synchronized by deleting the key-value pairs that are reclaimed by GC in the table.
This is the step in how the weak key is automatically removed from the Weakhashmap.

Like HashMap, Weakhashmap is not synchronized. You can use the Collections.synchronizedmap method to construct synchronized Weakhashmap.

The inheritance relationship of Weakhashmap is as follows

Java.lang.Object
        java.util.abstractmap<k, v>
              java.util.weakhashmap<k, v> public
     
class Weakhashmap<k,v>
    extends abstractmap<k,v>
    implements map<k,v> {}

Weakhashmap and map relate to the following diagram:

Constructor for Weakhashmap

Weakhashmap has a total of 4 constructors, as follows:

The default constructor.
Weakhashmap ()
     
//Specify the constructor for capacity size
weakhashmap (int capacity)
     
//Specify the constructor
for capacity size and load factor Weakhashmap (int capacity, float loadfactor)
     
//constructor with "sub Map"
weakhashmap (map<? extends K,? extends V> Map

API for Weakhashmap

void Clear                   ()
object                 Clone ()
Boolean                ContainsKey (Object key)
Boolean                containsvalue ( Object value)
set<entry<k, v>>       entryset ()
V get                      (Object key)
Boolean                IsEmpty ()
set<k>                 keyset ()
v put                      (k key, V value)
void                   putall (map<? extends K, ? Extends v> map)
V                      Remove (Object key)
int                    size ()
collection<v>          values ()


2nd part Weakhashmap Source Analysis

The following is a description of the source code for WEAKHASHMAP

Package java.util;
Import java.lang.ref.WeakReference;
     
Import Java.lang.ref.ReferenceQueue; public class Weakhashmap&lt;k,v&gt; extends abstractmap&lt;k,v&gt; implements map&lt;k,v&gt; {//default initial capacity
    The quantity is 16 and must be a power of 2.
     
    private static final int default_initial_capacity = 16;
     
    Maximum capacity (must be 2 power and less than 2 of 30 times, incoming capacity is replaced by this value) private static final int maximum_capacity = 1 &lt;&lt; 30;
     
    Default load factor private static final float default_load_factor = 0.75f;
    An array of entry that stores the data, with a power length of 2.
     
    Weakhashmap is implemented by the Zipper method, each Entry is essentially a one-way linked list private entry[] table;
     
    The size of the Weakhashmap, which is the number of key-value pairs weakhashmap saved, private int size;
     
    The WEAKHASHMAP threshold is used to determine whether the weakhashmap capacity (threshold = capacity * load factor) needs to be adjusted to private int threshold;
     
    Load factor actual size private final float loadfactor;
    The queue holds "weakly referenced keys" that have been cleared by GC. Weak references and referencequeue are used jointly: if the weak reference object is garbage collected, the Java virtual machine adds the weak reference to the reference queue associated with it private final referencequeue&lt;k&gt; Queue = new REferencequeue&lt;k&gt; ();
     
    Weakhashmap changed the number of times private volatile int modcount; Specifies the constructor for capacity size and load factor public weakhashmap (int initialcapacity, float loadfactor) {if (Initialcapacity &lt; 0
                                               ) throw new IllegalArgumentException ("Illegal Initial Capacity:" +
        initialcapacity); The maximum capacity of the Weakhashmap can only be maximum_capacity if (initialcapacity &gt; maximum_capacity) initialcapacity = MA
     
        ximum_capacity; if (loadfactor &lt;= 0 | |
                                               Float.isnan (Loadfactor)) throw new IllegalArgumentException ("illegal Load factor:" +
        Loadfactor);
        Find the Power int capacity = 1 of the smallest 2 "greater than initialcapacity";
        while (capacity &lt; initialcapacity) capacity &lt;&lt;= 1;
        Creates a Entry array to hold the data table = new Entry[capacity];
        Set "load factor" this.loadfactor = Loadfactor; Set the WeakhaShmap threshold ", when the amount of data stored in the Weakhashmap reaches threshold, it needs to double the capacity of the weakhashmap.
    threshold = (int) (capacity * loadfactor); }//Specify the constructor for capacity size public weakhashmap (int initialcapacity) {This (initialcapacity, default_load_fact
    OR);
    }//default constructor.
        Public Weakhashmap () {this.loadfactor = Default_load_factor;
        threshold = (int) (default_initial_capacity);
    Table = new Entry[default_initial_capacity]; The constructor with the "child Map" public weakhashmap (map&lt;? extends K,? extends v&gt; m) {This math.max ((int) (
        M.size ()/Default_load_factor) + 1, default_load_factor);
    Adds all the elements in M to the Weakhashmap Putall (m) individually;
    }//Mask value with null key.
    Because "null key" is allowed in WeakReference, it is deleted if it is inserted directly into "null key" as a weak reference.
    Therefore, here for "key null" empty, are uniformly replaced by "key is Null_key", "Null_key" is "static final constant."
     
    private static final Object Null_key = new Object (); Special handling for "null key" private static Object mAsknull (Object key) {return (key = null?)
    Null_key:key); }//restore special handling for "NULL key" private static &lt;K&gt; K unmasknull (Object key) {return (k) (key = = Null_ KEY?
    Null:key);
    }//Judge whether "X" and "Y" are equal static Boolean Eq (object x, Object y) {return x = = y | | x.equals (y);  ///Return index value/h &amp; (LENGTH-1) guaranteed return value less than length static int indexfor (int h, int length) {returns h
    &amp; (Length-1); }//empty null key value pairs in table.
    The principle is as follows://(01) When a "weakly referenced key" in Weakhashmap is retrieved by GC because it is not referenced again,//the "weak reference key" that is recycled is also added to "Referencequeue (queue)". (02) When we execute expungestaleentries,//walk through all keys in Referencequeue (queue)//Then delete the "refer" in "WeakReference table"
        Key in Encequeue (queue) corresponds to the private void Expungestaleentries () {entry&lt;k,v&gt; E;
            while ((E = (entry&lt;k,v&gt;) queue.poll ())!= null) {int h = E.hash;
     
          int i = indexfor (h, table.length);  Entry&lt;k,v&gt; prev = table[i];
            entry&lt;k,v&gt; p = prev;
                while (P!= null) {entry&lt;k,v&gt; next = P.next;
                    if (p = = e) {if (prev = e) table[i] = next;
                    else Prev.next = next;  E.next = null; Help GC e.value = null;
                    "" size--;
                Break
                } prev = P;
            p = Next; }}//Get Weakhashmap Table (array of key value pairs) Private entry[] GetTable () {//delete table "K recovered by GC
        EY the corresponding key value pairs "expungestaleentries ();
    return table;
        //Get Weakhashmap's actual size public int size () {if (size = 0) return 0;
        Deletes the key value pair for the key that has been collected by GC in Table Expungestaleentries ();
    return size; public Boolean IsEmpty () {return SiZe () = = 0;
        //Get key corresponding to the value public V gets (object key) {Object k = Masknull (key);
        Gets the hash value of the key.
        int h = Hashmap.hash (K.hashcode ());
        entry[] tab = gettable ();
        int index = indexfor (h, tab.length);
        Entry&lt;k,v&gt; e = Tab[index];
                Find the element with key value equal to key on the linked list for this hash value while (e!= null) {if (E.hash = = h &amp;&amp; eq (k, E.get ()))
            return e.value;
        e = E.next;
    return null;
    }//Weakhashmap contains key public boolean containskey (Object key) {return Getentry (key)!= null;
        //Returns the key value of key to Entry&lt;k,v&gt; getentry (object key) {Object K = Masknull (key);
        int h = Hashmap.hash (K.hashcode ());
        entry[] tab = gettable ();
        int index = indexfor (h, tab.length);
        Entry&lt;k,v&gt; e = Tab[index]; while (e!= null &amp;&amp;!) ( E.hash = = h &amp;&amp; eq (k, E.get ())) E = e.Next
    return e;
        ///Add "Key-value" to the Weakhashmap public V-put (k key, V value) {k k = (k) masknull (key);
        int h = Hashmap.hash (K.hashcode ());
        entry[] tab = gettable ();
     
        int i = indexfor (h, tab.length); for (entry&lt;k,v&gt; e = tab[i]; e!= null; e = e.next) {//if the corresponding key value pair already exists, replace the old value with the new value.
            And then quit!
                if (h = = E.hash &amp;&amp; eq (k, E.get ())) {V oldValue = E.value;
                if (value!= oldValue) E.value = value;
            return oldValue;
        }//If the corresponding key value pair does not exist in Weakhashmap, add "Key-value" to the table modcount++;
        Entry&lt;k,v&gt; e = tab[i];
        Tab[i] = new Entry&lt;k,v&gt; (K, value, queue, H, e);
        if (++size &gt;= threshold) Resize (tab.length * 2);
    return null; //Resize the Weakhashmap, newcapacity is the adjusted unit void resize (int newcapacity) {Entry[] oldtable = gettable ();
        int oldcapacity = Oldtable.length;
            if (oldcapacity = = maximum_capacity) {threshold = Integer.max_value;
        Return
        //Create a new newtable, add all the elements of the old table to the new newtable,//Then assign the new newtable to the old table.
        entry[] newtable = new Entry[newcapacity];
        Transfer (oldtable, newtable);
     
        Table = newtable;
        if (size &gt;= threshold/2) {threshold = (int) (newcapacity * loadfactor);
            else {//delete the key value pair of the key that has been collected by GC in the table expungestaleentries ();
            Transfer (newtable, oldtable);
        Table = oldtable; }///Add all elements in Weakhashmap to newtable private void transfer (entry[) SRC, entry[] dest) {for (i NT J = 0; J &lt; Src.length;
            ++J) {entry&lt;k,v&gt; e = src[j];
            SRC[J] = null;
     while (e!= null) {entry&lt;k,v&gt; next = E.next;           Object key = E.get ();  if (key = = null) {e.next = null; Help GC e.value = null;
                "" size--;
                    else {int i = indexfor (E.hash, dest.length);
                    E.next = Dest[i];
                Dest[i] = e;
            } e = next;
        Add all elements of "M" to weakhashmap public void Putall (map&lt;? extends K,? extends v&gt; m) {
        int numkeystobeadded = M.size ();
     
        if (numkeystobeadded = = 0) return;
        Calculate capacity is sufficient,//if "current actual capacity &lt; required capacity", the capacity is X2.
            if (numkeystobeadded &gt; Threshold) {int targetcapacity = (int) (numkeystobeadded/loadfactor + 1);
            if (targetcapacity &gt; maximum_capacity) targetcapacity = maximum_capacity;
            int newcapacity = Table.length; while (Newcapacity &lt; Targetcapacity) newcapacity &lt;&lt;= 1;
        if (newcapacity &gt; Table.length) Resize (newcapacity);
        //Add the elements in "M" to the weakhashmap individually.
    for (map.entry&lt. Extends K,? extends V&gt; E:m.entryset ()) put (E.getkey (), E.getvalue ());
        //Remove the key to key element public V remove (object key) {Object k = Masknull (key);
        Gets the hash value.
        int h = Hashmap.hash (K.hashcode ());
        entry[] tab = gettable ();
        int i = indexfor (h, tab.length);
        Entry&lt;k,v&gt; prev = tab[i];
     
        entry&lt;k,v&gt; e = prev;
            Delete the "key to Key" element//essence of the list is "delete nodes in one-way list" while (e!= null) {entry&lt;k,v&gt; next = E.next;
                if (h = = E.hash &amp;&amp; eq (k, E.get ())) {modcount++;
                size--;
                if (prev = = e) Tab[i] = next;
                else Prev.next = next; Return E.value;
            } prev = e;
        e = next;
    return null; }//delete "key value pair" Entry&lt;k,v&gt; removemapping (Object o) {if (!) (
        o instanceof map.entry) return null;
        entry[] tab = gettable ();
        Map.entry Entry = (map.entry) o;
        Object k = Masknull (Entry.getkey ());
        int h = Hashmap.hash (K.hashcode ());
        int i = indexfor (h, tab.length);
        Entry&lt;k,v&gt; prev = tab[i];
     
        entry&lt;k,v&gt; e = prev;
            Delete the "key value pair E"//essence of the list is "delete the node in one-way list" while (e!= null) {entry&lt;k,v&gt; next = E.next;
                if (h = = E.hash &amp;&amp; e.equals (entry)) {modcount++;
                size--;
                if (prev = = e) Tab[i] = next;
                else Prev.next = next;
            return e;
            } prev = e;
        e = next; } return null;
     
        }//Empty Weakhashmap, set all elements to null public void clear () {while (queue.poll ()!= null);
        modcount++;
        entry[] tab = table;
        for (int i = 0; i &lt; tab.length ++i) tab[i] = null;
     
        size = 0;
    while (Queue.poll ()!= null); Whether or not the element public boolean containsvalue (Object value) {//that contains value = =//If "value is null", calls the CONTAINSNULLV
     
        Alue () find if (value==null) return Containsnullvalue ();
        If "value is not NULL", find out if there are any nodes in the weakhashmap that have values of value.
        entry[] tab = gettable (); for (int i = Tab.length i--&gt; 0;) for (Entry e = tab[i]; e!= null; e = e.next) if (VA
        Lue.equals (E.value)) return true;
    return false;
        //whether contains null values private Boolean containsnullvalue () {entry[] tab = gettable ();
         for (int i = Tab.length i--&gt; 0;)   for (Entry e = tab[i], e!= null; e = e.next) if (e.value==null) return true;
    return false;
    }//Entry is a one-way linked list.
    It is the "Weakhashmap chain Storage Method" corresponding to the linked list. It implements the Map.entry interface, that is, implementing Getkey (), GetValue (), SetValue (V value), Equals (Object O), hashcode () These functions are private static class
        Entry&lt;k,v&gt; extends weakreference&lt;k&gt; implements map.entry&lt;k,v&gt; {private V value;
        private final int hash;
     
        Point to next node private entry&lt;k,v&gt; next;
        The constructor function.
            Entry (K key, V value, referencequeue&lt;k&gt; queue, int hash, entry&lt;k,v&gt; next) {
            Super (key, queue);
            This.value = value;
            This.hash = hash;
        This.next = Next;
        Public K Getkey () {return weakhashmap.&lt;k&gt;unmasknull ());
        Public v. GetValue () {return value; } PubLic v SetValue (v newvalue) {v oldValue = value;
            value = newvalue;
        return oldValue;
        //Determine if two entry are equal//If two entry "key" and "value" are equal, return true. Otherwise, returns false public 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 &amp;&amp; k1.equals (K2)))
                {Object V1 = GetValue ();
                Object v2 = E.getvalue (); if (v1 = = V2 | |
                    (v1!= null &amp;&amp; v1.equals (v2)))
            return true;
        return false;
            //Implementation hashcode () public int hashcode () {Object k = Getkey ();
            Object v = getValue ();
        Return ((K==null 0:k.hashcode ()) ^ (V==null 0:v.hashcode ()));
 }    
        Public String toString () {return getkey () + "=" + GetValue ();
    }//Hashiterator is the abstract parent class of the Weakhashmap iterator and implements a public function.
    It contains 3 subclasses of "key iterator (Keyiterator)", "Value iterator (Valueiterator)", and "entry iterator (Entryiterator)".
        Private abstract class Hashiterator&lt;t&gt; implements iterator&lt;t&gt; {//current index int index;
        The current element entry&lt;k,v&gt; Entry = null;
        Last returned element entry&lt;k,v&gt; lastreturned = null;
        Expectedmodcount is used to implement the fast-fail mechanism.
     
        int expectedmodcount = Modcount;
     
        Next key (strong reference) Object Nextkey = null;
     
        The current key (strong reference) Object Currentkey = null;
        Constructor Hashiterator () {index = (size ()!= 0 table.length:0);
     
            }//Whether there is the next element public boolean Hasnext () {entry[] t = table;
            A entry is a one-way list//if the next node of the entry is not empty, next is pointed to a node down; Otherwise, the NEX will beT points to the entry node of the next linked list (also the next one).
                while (Nextkey = = null) {entry&lt;k,v&gt; e = Entry;
                int i = index;
                while (E = = null &amp;&amp; i &gt; 0) e = t[--i];
                Entry = e;
                index = i;
                    if (E = = null) {Currentkey = null;
                return false; } Nextkey = E.get ();
            Hold on to key in strong ref if (Nextkey = null) entry = Entry.next;
        return true;
                //Get Next element protected entry&lt;k,v&gt; NextEntry () {if (Modcount!= expectedmodcount)
            throw new Concurrentmodificationexception ();
     
            if (Nextkey = = null &amp;&amp;!hasnext ()) throw new Nosuchelementexception ();
            lastreturned = entry;
            Entry = Entry.next;
    Currentkey = Nextkey;        Nextkey = null;
        return lastreturned; }//delete current element public void remove () {if (lastreturned = null) throw new I
            Llegalstateexception ();
     
            if (Modcount!= expectedmodcount) throw new Concurrentmodificationexception ();
            WeakHashMap.this.remove (Currentkey);
            Expectedmodcount = Modcount;
            lastreturned = null;
        Currentkey = null; }//value iterator private class Valueiterator extends hashiterator&lt;v&gt; {public V Next
        () {return nextentry (). value;
            }//Key iterator private class Keyiterator extends hashiterator&lt;k&gt; {public K next () {
        Return NextEntry (). Getkey ();
        }///Entry Iterator private class Entryiterator extends Hashiterator&lt;map.entry&lt;k,v&gt;&gt; { Public map.entry&lt;k,v&gt; Next () {RetuRN NextEntry ();
     
    The Entry corresponding set of}//Weakhashmap is private transient set&lt;map.entry&lt;k,v&gt;&gt; entryset = null;
        Returns a "key collection" that actually returns a "keyset object" public set&lt;k&gt; keyset () {set&lt;k&gt; KS = keyset;
    return (KS!= null KS: (keyset = new Keyset ()));
    The set//keyset corresponding to the//key inherits from Abstractset, stating that there are no duplicate keys in the collection. Private class Keyset extends Abstractset&lt;k&gt; {public iterator&lt;k&gt; iterator () {return new K
        Eyiterator ();
        public int size () {return WeakHashMap.this.size ();
        Public Boolean contains (Object o) {return containskey (o); public boolean remove (Object o) {if (ContainsKey (o)) {WeakHashMap.this.remov
                E (O);
            return true;
        else return false; public void Clear () {Weakhashmap. This.clear (); }//returns "Value collection", actually returns a values object public collection&lt;v&gt; values () {collection&lt;v&gt; V
        s = values;  return (vs!= null?)
    VS: (values = new values ()); }//"Value set"//values inherits from Abstractcollection, unlike "keyset inherited from Abstractset",//the elements in values can be duplicated.
    Because a different key can point to the same value. Private class Values extends abstractcollection&lt;v&gt; {public iterator&lt;v&gt; iterator () {retur
        N New Valueiterator ();
        public int size () {return WeakHashMap.this.size ();
        Public Boolean contains (Object o) {return containsvalue (o);
        public void Clear () {WeakHashMap.this.clear (); }//Return "Weakhashmap Entry Collection"//It actually returns a EntrySet object public set&lt;map.entry&lt;k,v&gt;&gt; EntrySet
        () {set&lt;map.entry&lt;k,v&gt;&gt; es = EntrySet; Return es!= null? ES: (EntrySet = new EntrySet ());
    The//EntrySet corresponding Set//EntrySet inherits from Abstractset, stating that there are no duplicate entryset in the collection. Private class EntrySet extends abstractset&lt;map.entry&lt;k,v&gt;&gt; {public ITERATOR&LT;MAP.ENTRY&LT;K,V&GT;&G T
        Iterator () {return new entryiterator (); //contains the value (O) public boolean contains (Object o) {if (!
            o instanceof Map.entry) return false;
            Map.entry e = (map.entry) o;
            Object k = E.getkey ();
            Entry candidate = Getentry (E.getkey ());
        Return candidate!= null &amp;&amp; candidate.equals (e);
        }//delete the value (O) Public boolean remove (Object o) {return removemapping (o)!= null;
        //Returns the size of weakhashmap public int size () {return WeakHashMap.this.size ();
        //empty Weakhashmap public void Clear () {WeakHashMap.this.clear ();
  }   
        Copy functions. Copy all elements in the Weakhashmap to the list private list&lt;map.entry&lt;k,v&gt;&gt; deepcopy () {list&lt;map.entry&lt ;
            k,v&gt;&gt; list = new arraylist&lt;map.entry&lt;k,v&gt;&gt; (size ());
            for (map.entry&lt;k,v&gt; e:this) List.add (new Abstractmap.simpleentry&lt;k,v&gt; (e));
        return list;
        //Returns entry corresponding object[] array public object[] ToArray () {return deepcopy (). ToArray (); //Return entry corresponding t[] Array (t[] The array type defined when we create a new array) public &lt;T&gt; t[] ToArray (t[) a) {retur
        n deepcopy (). ToArray (a); }
    }
}

See more highlights of this column: http://www.bianceng.cnhttp://www.bianceng.cn/Programming/Java/

Description

Related Article

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.