Source analysis: Hashmap

xiaoxiao2021-03-06  52

Hashmap is an implementation used in Java New Collection Framework, and the difference between HashMap and HashTable is that HashMap is not synchronized and allows NULL values. HashTable inherits Dictionary and uses Enumeration, so it is recommended not to use. HashMap statement as follows: public class HashMap extends AbstractMap implements Map, Cloneable, Serializable about AbstractMap: http: //blog.9cbs.net/treeroot/archive/2004/09/20/110343.aspx about Map: http: // blog .9cbs.net / Treeroot / Archive / 2004/09/20 / 110331.aspx About Cloneable: http://blog.9cbs.net/treeroot/archive/2004/09/07/96936.aspx This class is more complicated, here It is only a few ways to analyze several methods, especially the last few internal classes, but it is more simple.

Static Final Int default_initial_capacity = 16; default initialization

Static Final Int Maximum_capacity = 1 << 30; Maximum Initialization Size

Static final float default_load_factor = 0.75f; default add factor

Transient entry [] Table; an array of entry types, an index of the array length 2.

TRANSIENT INT SIZE; Map of Map

INT THRESHOLD; the value of the next expansion

Final float loadfactor; loading factor

TRANSIENT VOLATILE INT MODMOUNT;

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); int capacity = 1; while (capacity

Public HashMap (int initialcapacity) {this (Initialcapacity, default_load_factor);

Public hashmap () {this.loadfactor = default_load_factor; threshold = (int)); Note: This should be a mistake! It should be: threshold = (int) (default_initial_capacity * loadfactor); table = new entry [default_initial_capacity]; init ();} public hashmap (map m) {this (math.max ((int) (m.size () / DEFAULT_LOAD_FACTOR) 1, default_initial_capacity, default_load_factor; putallForcreate (m);}

Void init () {}

Static Final Object Null_Key = New Object ();

Static Object Masknull (Object Key) {return (key == null? null_key: key);

Static Object UNMASKNULL (Object Key) {return (key == null_key? null: key);

Static int Haash (Object X) {INT H = x.hashcode (); h = ~ (h << 9); h ^ = (h >>> 14); h = (h << 4); h ^ = (h >>> 10); RETURN H;} There is no way in HashTable, that is, HashTable is a HashCode value of the object with the object, but hashmap has improved the algorithm to obtain a hash value.

Static Boolean EQ (Object X, Object Y) {Return X == Y || x.equals (y);

Static int indexFor (INT H, INT Length) {RETURN H & (LENGTH-1);} Returns the location of the Hash value in array according to the length of the hash value and array, is just a simple relationship.

Public int size () {return size;}

Public Boolean ISempty () {return size == 0;

Public Object Get {Object K = Masknull (key); int has = hash (k); int I = indexfor (hash, table.Length); entry E = Table [i]; while (true) {i (e == NULL) RETURN E; if (E.hash == Hash && EQ (K, E.Key)) Return E.Value; E = E.NEXT;}} This method is to obtain data, first get Hash value, cover the NULL value, and the HASH value is corrected by the function hash (). Then calculate the index value of the hash value in the array. If the reference is referenced to NULL, it indicates that this mapping is not present in HashMap. Otherwise, the whole linked list is all over the list. Here is to return, if you are not found, it is traversed to the end of the chain, and returns NULL. The comparison here is like this: E.hash == Hash && EQ (K, E.Key) That is to say, if the HASH is definitely considered non-equal, EQ is shorted, only in the same situation in Hash, only call Equals. method. Now we should understand that if the two objects Equals returns True, their hashcode should the same truth. If two object calls Equals returns True, but hashcode is different, then they think they are not equal in HashMap. Public Boolean Containskey (Object Key) {Object K = Masknull (Key); Int Hash = Hash (K); INT I = IndexFor (Hash, Table.Length); Entry E = Table [I]; While (E! = null ) {IF (E.hash == Hash && EQ (K, E.key)) Return True; E = E.Next;} Return False;} This method is more simple than the above, first find the hash position, and then traverse the whole Link list, return to TRUE if you find it. Entry GetEntry (Object Key) {

Object K = Masknull (key);

INT has = hash (k);

INT I = IndexFor (Hash, Table.Length);

Entry E = Table [I];

While (e! = null&&! (E.hash == Hash && EQ (K, E.Key))))))

E = E.NEXT;

Return E;

}

This method returns an ENTRY node according to the key value, but also gets the index position, traverses the next chain, and if the return is NULL.

Public Object Put (Object Key, Object Value) {Object K = Masknull (Key); Int Hash = Hash (K); INT I = IndexFor (Hash, Table.Length); for (Entry E = Table [i]; E ! = null; e = E.Next) {if (E.hash == Hash && Eq (K, E.key)) {Object OldValue = E.Value; E.Value = Value; E.RecordAccess (this); Return OldValue;}} modcount ; addentry (hash, k, value, i); return null;} First get the Hash index location, if the reference is reference to null, then insert a mapping, return null. If the reference is not null, you must traverse the chain list. If you find the same key, then update the value, return the original value value. If it is not found in traversal, it means that the Key value does not exist or inserts a mapping. If the Hash value is enough to discrete, that is, if the index is not used, then no traverse is a list. Conversely, if the HASH value is not discrete, the extreme, if it is constant, all mappings will be extremely low on this chain. Here is the simplest example, write two different classes as keys inserted into havehmap, and efficiency will be far different. Class Good {Int i; public good (int i) {this.i = i;} public boolean equals (object o) {Return (o instanceof good) && (this.i == ((good) O) .i). PUBLIC INT HashCode () {RETURN I;}} Class Bad {INT I; Public Bad (INT i) {this.i = i;} Public Boolean Equals (Object O) {Return (O InstanceOf Bad) && (this. i == ((BAD) O) .I)} public int hashcode () {return 0;}} execution code: map m1 = new hashmap (); map m2 = new hashmap (); for (int i = 0; I <100; i ) {m1.put (New Good (i), new integer (i)); // The efficiency here is very high} for (int i = 0; i <100; i ) {m2.put (New Bad (i), new integer (i)); // Almost there is almost two very extreme examples, and if you don't know how much difference is performed.

Private Void Putforcreate (Object Key, Object Value) {Object K = Masknull (key); int has = hash (k); int = indexfor (hash, table.Length); for (Entry E = Table [i]; e = NULL; E. E.Hash == Hash && EQ (K, E.Key)) {E.Value = Value; Return;}} CreateEntry (Hash, K, Value, i) } void putallForcreate (MAP M) {for (Iterator i = m.enTryset (). Iterator (); I.hasNext ();) {map.entry E = (map.entry) i.next (); PutforcReate E.GetKey (), E.GETVALUE ());}} The above two methods are called by constructor and clone methods.

void resize (int newCapacity) {Entry [] oldTable = table; int oldCapacity = oldTable.length; if (size newCapacity) return; Entry [] newTable = new Entry [newCapacity]; transfer (newTable); Table = newTable; threshold = (int);} This method reassocates space when needed, which is equivalent to ArrayList's EnSureCapacity method, but this is more complicated.

Void Transfer (entry [] src = table; int newcapacity = newTable.Length; for (int J = 0; j

Public void Putall (MAP T) {INT N = T.Size (); if (n == 0) Return; if (n> = threshold) {n = (int) (N / LoadFactor 1); if (N > Maximum_capacity) n = maximum_capacity; int Capacity = Table.Length; while (capacity

Entry RemoventryForKey (Object Key) {Object K = Masknull (key); int has = hash (k); int = indexfor (hash, table.Length); entry prev = table [i]; entry E = prev; while e! = null) {If e == null means there is no entry next = E.Next; if (E.hash == Hash && EQ (K, E.Key)) {modcount ; size--; if (prev = = e) Table [i] = next; the first element of the list is to delete, here is best to add an E.Next = NULL. Else prev.next = next; existence but not the first element of the list, here It is best to add E.Next = null. E.Recordremoval (this); return e;} prev = E; E = next;} return e; here is actually return null;} This method is not complicated, and the traversal table Here, it is recommended to add an E.Next = NULL, which can be changed to if (prev == e) table [i] = next; else prev.next = next; E.NEXT = NULL; this sentence is more, it can improve efficiency . Here is a brief explanation: because E is the deleted node, delete it is actually pointing to its pointer to a node behind it. Therefore, E can be used as an object recovered by GC. There is also a NEXT pointer to our data. If e is not recycled, and at this time, the node points to the node becomes useless, but there is a reference to its reference (E.Next), so although the next node is useless, but not As the object recovered by GC, unless e first recovery. Although it is not necessarily a big problem, it will at least affect the recovery efficiency of GC. Just like the foreign key references in the database, it is very troublesome to delete it.

Entry RemoveMapping (Object O) {if (! (O instanceof map.entry) Return null; map.entry entry = (map.entry) O; object k = masknull (entry.getKey ()); int hash = hash K); int I = indexfor (haveh, table.Length); entry prev = Table [i]; entry E = prev; while (e! = null) {entry next = E.NEXT; if (E.hash == Hash && e.equals (entry)) {modcount ; size -; if (prev == e) Table [i] = next; else prev.next = next; E.Recordremoval (this); Return E;} prev = E; E = Next;} Return E;} This method is the same as above. Public void clear () {modcount ; entry tab [] = table; for (int i = 0; i

Public Boolean ContaSvalue ({if (value == null) Return ContainSnullValue (); entry tab [] = table; for (int i = 0; i

Private Boolean ContainSnullValue () {entry Tab [] = Table; for (int i = 0; i

public Object clone () {HashMap result = null; try {result = (HashMap) super.clone ();} catch (CloneNotSupportedException e) {// assert false;} result.table = new Entry [table.length]; result .entrySet = null; result.modCount = 0; result.size = 0; result.init (); result.putAllForCreate (this); return result;} static class Entry implements Map.Entry {final Object key; Object value; final INTHESH; Entry Next; Entry (int h, object k, object v, entry n) {value = v; next = n; key = k; hash = h;} public object getKey () {Return unmasknull (key); } public Object getValue () {return value;} public Object setValue (Object newValue) {Object oldValue = value; value = newValue; return oldValue;!} 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! = Nu LL && K1.Equals (k2))) {Object v1 = getValue (); object v2 = E.GETVALUE (); if (v1 == V2 || (v1! = null && v1.equals (v2))) Return True;} returnaf ()} public int 6code () {return (key == null_key? 0: key.hashcode ()) ^ (value == null? 0: value.hashcode ());} public string toString () {Return getKey () "=" getValue ();} void recordaccess (HashMap M) {} void recordremoval (havehmap m) {}} A static internal class

void addEntry (int hash, Object key, Object value, int bucketIndex) {table [bucketIndex] = new Entry (hash, key, value, table [bucketIndex]); if (size > = threshold) resize (2 * table.length ); Note this method, insert the head of the same table. This can be written better understood: Entry oldHead = table [bucketIndex]; Entry newHead = new Entry (hash, key, value, oldHead); table [bucketIndex] = newHead; void createEntry (int hash, Object key, Object value, int BucketIndex) {Table [BucketIndex] = New Entry (Hash, Key, Value, Table [BucketIndex]); SIZE ;}

private abstract class HashIterator implements Iterator {Entry next; int expectedModCount; int index; Entry current; HashIterator () {expectedModCount = modCount; Entry [] t = table; int i = t.length; Entry n = null; if (size! = 0) {While (n = t [n =]) == null);} next = n; index = i;} public boolean Hasnext () {Return next! = Null;} entry nextentry () {if (! modCount = expectedModCount) throw new ConcurrentModificationException (); Entry e = next; if (e == null) throw new NoSuchElementException (); Entry n = e.next; Entry [] t = table; int i = index; while (n == NULL && I> 0) n = t [- i]; index = i; next = n; return current = e;} public void remove () {if (current == null) Throw new IllegalStateException (); if (MODCOUNT! = Expectedmodcount) throw new concurrentmodificationException (); Object K = Current.Key; Cu Rrent = null; hashmap.this.removeentryforKey (k); expectedmodcount = modcount;} }.com.

Private class keyiterator extends hashiterator {public object next () {return next (). getKey ();}}

Private class entryiterator extends hashiterator {public object next () {return next ();}}

Iterator newkeyiterator () {return new keyiterator ();

Iterator newvalueiterator () {return new valueiterator ();}

Iterator newntryiterator () {return new entryiterator ();} private transient set entryset = NULL

Public set keyset () {set ks = keyset; return (ks! = null? ks: (keyset = new keyset));}

private class KeySet extends AbstractSet {public Iterator iterator () {return newKeyIterator ();} public int size () {return size;} public boolean contains (Object o) {return containsKey (o);} public boolean remove (Object o) {Return HashMap.This.RemoventryForKey (o)! = Null;} public void clear () {hashmap.this.clear ();}}

Public Collection Values ​​() {Collection VS = VALUES; RETURN (VS! = NULL? VS: (VALUES = New Values ​​()));}

private class Values ​​extends AbstractCollection {public Iterator iterator () {return newValueIterator ();} public int size () {return size;} public boolean contains (Object o) {return containsValue (o);} public void clear () {HashMap .THIS.CLEAR ();}}

Public set entryset () {set es = entryset; return (es! = null? es: (entryset = new entryset));

private class EntrySet extends AbstractSet {public Iterator iterator () {return newEntryIterator ();} public boolean contains (Object o) {if (! (o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry) O; entry candidate = getentry (E.GetKey ()); return Candidate! = Null && Candidate.equals (e);} public boolean remove (Object O) {Return RemoveMapping (o)! = null;} public int size ) {Return size;} public void clear () {hashmap.this.clear ();}}

private void writeObject (java.io.ObjectOutputStream s) throws IOException {s.defaultWriteObject (); s.writeInt (table.length); s.writeInt (size); for (Iterator i = entrySet () iterator ();. i .hasNext (); {map.entry E = (map.entry) i.next (); s.writeObject (E.GetKey ()); s.writeObject (E.GetValue ());}} private static final Long serialversionuid = 362498820763181265L;

private void readObject (java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {s.defaultReadObject (); int numBuckets = s.readInt (); table = new Entry [numBuckets]; init (); size = s.readInt () ; for (int i = 0; for (int i = 0; i

Object key = s.readObject ();

Object value = s.readObject ();

PutforcReate (key, value);

}

}

INT Capacity () {Return Table.Length;} Float LoadFactor () {Return LoadFactor;

转载请注明原文地址:https://www.9cbs.com/read-114931.html

New Post(0)