HashMap insufficient analysis

Insufficiency:

1. The flaw lies in its high dependence hash algorithm, if the key is a custom class, you have to rewrite your own hashcode method, write hash algorithm.

And hashmap requirements, when hashcode into what, and then it can not be changed, if a class hashcode its members about the variable name, and then name changed again, then the hashmap behavior is not normal.

If two objects equals the same value that hashcode necessarily the same, if the same hashcode value, the object is not necessarily the same as equals, can only prove that two objects at the same location in the hash store! The hash stored in the storage elements, typically first hash value is determined, this determination is not in position, and then determine equals the deposited elements are equal.

So hash value and must have an object property relations, otherwise it can not guarantee equal hash equals on the other, but linked to property and, once the property changes, it changes hash, hash stored in the location will change

2.hashmap element storage locations, in addition to the hash value of the relevant key elements, but also with the length of the array itself, and if a change in the length of the array expansion, all the elements necessary to recalculate its storage position index, determined beforehand as much as possible the size hashmap, prevent expansion

1 , the basic concept:

Hash Hash: The hash conversion algorithm to a fixed value

The Map : the X-, the y- map

By Hash targeting values to the Map , the Value deposit into

Storage: k: v way. Key-value pairs

key can be null, null as a key to store

 

2 , source code analysis:

1、static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

Default initial capacity of 2 to 4 th

2、static final int MAXIMUM_CAPACITY = 1 << 30;

The maximum capacity of 2 to 30 th

3static final float DEFAULT_LOAD_FACTOR = 0.75f;

Load Factor: 0.75f , refers to the capacity of the container . 4 parts per . 3 , the capacity expansion

4int threshold;

Entrance to initialize - variable expansion

5、transient Node<K,V>[] table;

For the first time at the initial time, and resize as needed. When the distribution length is always 2 exponentiation

6、transient Set<Map.Entry<K,V>> entrySet;

Storing buffer the entrySet () , used to access Key , values

7transient int size;

The number of keys contained in this map mapping

8transient int modCount;

To record the number of modified

9, the final  float  load factor;

Pro load factor at

/**

* initialCapacity: initial capacity

loadFactor * : Load factor

*/

10、public HashMap(int initialCapacity, float loadFactor)

A constructor argument

11、public HashMap(int initialCapacity)

12、public HashMap()

Initialization load factor

13、public HashMap(Map<? extends K, ? extends V> m)

To achieve the map. putAll and Map constructor.

 

14、 public V put(K key, V value) {

        return putVal(hash(key), key, value, false, true);

}

Associated with the specified key value specified in this map. If the map previously contained a mapping key, the old value is replaced.

 

  final V putVal(int hash, K key, V value, boolean onlyIfAbsent,

                   boolean evict) {

        Node<K,V>[] tab; Node<K,V> p; int n, i;

        if ((tab = table) == null || (n = tab.length) == 0)

            n = (tab = resize()).length;

        if ((p = tab[i = (n - 1) & hash]) == null)

            tab[i] = newNode(hash, key, value, null);

        else {

            Node<K,V> e; K k;

            if (p.hash == hash &&

                ((k = p.key) == key || (key != null && key.equals(k))))

                e = p;

            else if (p instanceof TreeNode)

                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

            else {

                for (int binCount = 0; ; ++binCount) {

                    if ((e = p.next) == null) {

                        p.next = newNode(hash, key, value, null);

                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st

                            treeifyBin(tab, hash);

                        break;

                    }

                    if (e.hash == hash &&

                        ((k = e.key) == key || (key != null && key.equals(k))))

                        break;

                    p = e;

                }

            }

            if (e != null) { // existing mapping for key

                V oldValue = e.value;

                if (!onlyIfAbsent || oldValue == null)

                    e.value = value;

                afterNodeAccess(e);

                return oldValue;

            }

        }

        ++modCount;

        if (++size > threshold)

            resize();

        afterNodeInsertion(evict);

        return null;

    }

15、 public V get(Object key) {

        Node<K,V> e;

        return (e = getNode(hash(key), key)) == null ? null : e.value;

}

- key parameter entity class

 static class Node<K,V> implements Map.Entry<K,V> {

        final int hash;

        final K key;

        V value;

        Node<K,V> next;

 

        Node(int hash, K key, V value, Node<K,V> next) {

            this.hash = hash;

            this.key = key;

            this.value = value;

            this.next = next;

        }

 

        public final K getKey()        { return key; }

        public final V getValue()      { return value; }

        public final String toString() { return key + "=" + value; }

 

        public final int hashCode() {

            return Objects.hashCode(key) ^ Objects.hashCode(value);

        }

 

        public final V setValue(V newValue) {

            V oldValue = value;

            value = newValue;

            return oldValue;

        }

 

        public final boolean equals(Object o) {

            if (o == this)

                return true;

            if (o instanceof Map.Entry) {

                Map.Entry<?,?> e = (Map.Entry<?,?>)o;

                if (Objects.equals(key, e.getKey()) &&

                    Objects.equals(value, e.getValue()))

                    return true;

            }

            return false;

        }

    }

- According to Kin find container for the presence of the Key , have returned V , no return null

final Node<K,V> getNode(int hash, Object key) {

        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;

        if ((tab = table) != null && (n = tab.length) > 0 &&

            (first = tab[(n - 1) & hash]) != null) {

            if (first.hash == hash && // always check first node

                ((k = first.key) == key || (key != null && key.equals(k))))

                return first;

            if ((e = first.next) != null) {

                if (first instanceof TreeNode)

                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);

                do {

                    if (e.hash == hash &&

                        ((k = e.key) == key || (key != null && key.equals(k))))

                        return e;

                } while ((e = e.next) != null);

            }

        }

        return null;

    }

Entry Object: the Table: Array + linked list data structure

  final Node<K,V>[] resize() {

        Node<K,V>[] oldTab = table;

        int oldCap = (oldTab == null) ? 0 : oldTab.length;

        int oldThr = threshold;

        int newCap, newThr = 0;

        if (oldCap > 0) {

            if (oldCap >= MAXIMUM_CAPACITY) {

                threshold = Integer.MAX_VALUE;

                return oldTab;

            }

            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&

                     oldCap >= DEFAULT_INITIAL_CAPACITY)

                newThr = oldThr << 1; // double threshold

        }

        else if (oldThr > 0) // initial capacity was placed in threshold

            newCap = oldThr;

        else {               // zero initial threshold signifies using defaults

            newCap = DEFAULT_INITIAL_CAPACITY;

            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

        }

        if (newThr == 0) {

            float ft = (float)newCap * loadFactor;

            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?

                      (int)ft : Integer.MAX_VALUE);

        }

        threshold = newThr;

        @SuppressWarnings({"rawtypes","unchecked"})

        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];

        = newtab table;

        if (oldTab != null) {

            for (int j = 0; j < oldCap; ++j) {

                Node<K,V> e;

                if ((e = oldTab[j]) != null) {

                    oldTab[j] = null;

                    if (e.next == null)

                        newTab[e.hash & (newCap - 1)] = e;

                    else if (e instanceof TreeNode)

                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);

                    else { // preserve order

                        Node<K,V> loHead = null, loTail = null;

                        Node<K,V> hiHead = null, hiTail = null;

                        Node<K,V> next;

                        do {

                            next = e.next;

                            if ((e.hash & oldCap) == 0) {

                                if (loTail == null)

                                    loHead = e;

                                else

                                    loTail.next = e;

                                loTail = e;

                            }

                            else {

                                if (hiTail == null)

                                    hiHead = e;

                                else

                                    hiTail.next = e;

                                hiTail = e;

                            }

                        } while ((e = next) != null);

                        if (loTail != null) {

                            loTail.next = null;

                            newtab [j] = loHead;

                        }

                        if (hiTail != null) {

                            hiTail.next = null;

                            newtab [j + oldCap] = hiHead;

                        }

                    }

                }

            }

        }

        Return  newtab;

    }

 

3, handwritten HashMap Source:

Top level interface Map:

 

com.cq.hashmap Package;
/ **
*
* @author M
*
* @param <K>
* @param <V>
* /
public interface the Map <K, V> {
/ **
* add content
* @param K
* v @param
* @return
* /
public PUT V (k K, V v);
/ **
* Key acquire the latest information in accordance
* @param k
* @return
* /
  public gET V (K k);
/ **
* Gets number of the container contents
* @return
* /
  public int size ();
/ **
* entity class interface
* @author Administrator
*
* @param <K>
* @param <V>
* /
  public interface the Entry <K, V> {
    public K getKey();
    public V getValue();
  }
}

Implementation class HashMap <k, v>

package com.cq.hashmap;

the HashMap class public <K, V> the implements the Map <K, V> {
// Default container
  Private static int. 1 << = defaultlegth. 4;
// load factor
  private static float defaultLoder = 0.75f;

  private Entry[] table = null;

  private int size = 0;

  public HashMap() {
    this(defaultlegth, defaultLoder);
  }

    public HashMap(int length, float defaultLoder2) {
      defaultlegth = length;
      defaultLoder = defaultLoder2;
      table = new Entry[defaultlegth];
  }


public V put(K k, V v) {
    size++;
    int index = hash(k);
    Entry<K, V> entry = table[index];
    if (entry == null) {
      table[index] = newEntry(k, v, null);
    } else {
      table[index] = newEntry(k, v, entry);
    }
    return (V) table[index].getValue();
  }

  public Entry<K, V> newEntry(K k, V v, Entry<K, V> next) {
    return new Entry<K,V>(k, v, next);
   }

  private Integer hash(K k) {
    int len = defaultlegth;
    int i = k.hashCode() % len;
    return i >= 0 ? i : -i;
   }


  public V get(K key) {
    int index = hash(key);
    if (table[index] == null) {
      return null;
    }
  return (V) find(key, table[index]);
  }

  private V find(K key, Entry<K, V> entry) {
    if (key == entry || key.equals(entry.getKey())) {
    if (entry.next != null) {
    System.out.println("旧值:" + entry.next.getValue());
  }
    return entry.getValue();
  } else {
  if (entry.next != null) {
  System.out.println("旧值:" + entry.next.getValue());
    find(key, entry.next);
  }
}
    return null;
}


public int size() {
  return size;
}

class Entry<K, V> implements Map.Entry<K, V> {
  K k;
  V v;
  Entry<K, V> next;

  public Entry(K k, V v, Entry<K, V> next) {
    super();
    this.k = k;
    this.v = v;
    this.next = next;
}


public K getKey() {
  return k;
}

public V getValue() {
  return v;
}

}

}

Test categories:

package com.cq.hashmap;

import org.junit.Test;

public class TestMap {
@Test
public void MapTest() {
Map<String, Integer> map=new HashMap<String, Integer>();
long currentTimeMillis = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
map.put("悟空"+i,i);
}

for (int i = 0; i < 1000; i++) {
System.out.println(map.get("悟空"+i));
}

long currentTimeMillis2 = System.currentTimeMillis();
System.out.println("大小:"+map.size()+"时间:"+(currentTimeMillis2-currentTimeMillis));
}
}

:( deficiencies scalability)

1. Scalability

2, time complexity: Your hash algorithm determines your efficiency

3, Key whether to repeat the relevant GET (0). 1
. 4 , when the hash expansion is the need to re- add entry inside the array.

How much capacity when needed, it is best to specify the size of the expansion, to prevent the put for expansion many times when

 

Guess you like

Origin www.cnblogs.com/yhm9/p/11569917.html