一,HashTable

哈希表,它相比于hashMap构造简略点,它没有波及红黑树,间接应用链表的形式解决哈希抵触。

咱们看它的字段,和hashMap差不多,应用table寄存元素

private transient Entry<?,?>[] table;private transient int count;private int threshold;private float loadFactor;private transient int modCount = 0;

它没有常量字段,默认值是在构造方法外面间接体现的,咱们看一下无参结构:

public Hashtable() {    this(11, 0.75f);}

1.get()办法

依据key取得value

public synchronized V get(Object key) {    Entry<?,?> tab[] = table;//计算下标    int hash = key.hashCode();    int index = (hash & 0x7FFFFFFF) % tab.length;//遍历查找,e=e.next    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {        if ((e.hash == hash) && e.key.equals(key)) {            return (V)e.value;        }    }    return null;}

2.put()办法

与get()办法相似,也是遍历table,而后调用addEntry()实现增加。

public synchronized V put(K key, V value) {    if (value == null) {        throw new NullPointerException();    }    Entry<?,?> tab[] = table;    int hash = key.hashCode();    int index = (hash & 0x7FFFFFFF) % tab.length;    @SuppressWarnings("unchecked")    Entry<K,V> entry = (Entry<K,V>)tab[index];//如果曾经存在,则笼罩,返回老的值    for(; entry != null ; entry = entry.next) {        if ((entry.hash == hash) && entry.key.equals(key)) {            V old = entry.value;            entry.value = value;            return old;        }    }//不存在,间接增加    addEntry(hash, key, value, index);    return null;}

addEntry()

private void addEntry(int hash, K key, V value, int index) {    modCount++;    Entry<?,?> tab[] = table;    if (count >= threshold) {    //大小超过阈值,要扩容        // Rehash the table if the threshold is exceeded        rehash();        tab = table;        hash = key.hashCode();        index = (hash & 0x7FFFFFFF) % tab.length;    }//增加    @SuppressWarnings("unchecked")    Entry<K,V> e = (Entry<K,V>) tab[index];    tab[index] = new Entry<>(hash, key, value, e);    count++;}

留神这里的手法,间接将新来的节点,放到头部,这样就能够不论前面是否存在节点,都不会呈现问题

protected Entry(int hash, K key, V value, Entry<K,V> next) {    this.hash = hash;    this.key =  key;    this.value = value;    this.next = next;}

二,HashMap

1.常量字段介绍

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 2的四次方,初始化默认的容量static final int MAXIMUM_CAPACITY = 1 << 30; 最大的容量值static final float DEFAULT_LOAD_FACTOR = 0.75f; //容量 负载因子,static final int TREEIFY_THRESHOLD = 8;        //链表转换为数的阈值static final int UNTREEIFY_THRESHOLD = 6;    //树转坏为链表的阈值static final int MIN_TREEIFY_CAPACITY = 64;    //桶中的数据采纳红黑树存储时,整个table的最小容量

字段:

transient Node<K,V>[] table; //存储骨干,节点数组transient Set<Map.Entry<K,V>> entrySet;transient int size;        //元素数量transient int modCount;        //批改次数//The next size value at which to resize (capacity * load factor).int threshold;  //下一次扩容的大小,final float loadFactor;    //负载因子

2.构造函数

2.1罕用的无参结构:

默认构造方法,就间接给负载因子赋值,其余没有操作,其余字段都是默认的。

// Constructs an empty <tt>HashMap</tt> with the default initial // capacity (16) and the default load factor (0.75).public HashMap() {    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted}

2.2带初始化容器大小,和负载因子的构造方法:

  首先要判断传入参数的正确性,而后赋值。

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);    this.loadFactor = loadFactor;    this.threshold = tableSizeFor(initialCapacity);}

2.3带汇合的构造方法:

传入一个Map汇合,调用put办法进行初始化。

public HashMap(Map<? extends K, ? extends V> m) {    this.loadFactor = DEFAULT_LOAD_FACTOR;    putMapEntries(m, false);}

从下面的代码中能够看到,在构造方法中并没有初始化table,具体的table初始化是在put操作上进行的。

3.增加

3.1 put()

是一个入口办法,理论调用的是putVal()办法,其中通过hash()办法计算了key对应的 值

public V put(K key, V value) {    return putVal(hash(key), key, value, false, true);}//异或运算,保障存储地位尽量均匀分布。static final int hash(Object key) {    int h;    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

具体的putVal()办法,内容很长

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,               boolean evict) {    Node<K,V>[] tab; Node<K,V> p; int n, i;  //变量初始化,n示意table的长度    if ((tab = table) == null || (n = tab.length) == 0)    //容器初始化        n = (tab = resize()).length;            //通过resize()办法获取调配空间。    if ((p = tab[i = (n - 1) & hash]) == null)    //如果新的地位是空的,则间接放入,否者要解决抵触        tab[i] = newNode(hash, key, value, null);        //将value封装成新的node    else {    //解决抵触        Node<K,V> e; K k;        // 留神p的赋值在 第二个if外面,它示意的是抵触地位所寄存的节点。如果新传入的节点和以后node的hash和key雷同,则上面再解决        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;            }        }            //更新以后传入的值到以后node中。返回之前的oldValue        if (e != null) { // existing mapping for key            V oldValue = e.value;            if (!onlyIfAbsent || oldValue == null)                e.value = value;            afterNodeAccess(e);            return oldValue;        }    }    ++modCount;    //批改次数+1,    if (++size > threshold)    //空间大小,如果超过了阈值,要扩容        resize();    afterNodeInsertion(evict);    return null;}

其中波及的重点办法:resize()办法,返回新调配的空间。

final Node<K,V>[] resize() {    Node<K,V>[] oldTab = table;//获取老的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 {               // 第一次初始化 oldCap=0,oldThr=0        newCap = DEFAULT_INITIAL_CAPACITY;    //默认大小,16        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);    //默认计算方法,16*0.75,12    }    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]; //重新分配空间。    table = newTab;    //扩容后的空间赋值(此时,空间还是空的)    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;}

4.Node构造

static class Node<K,V> implements Map.Entry<K,V> {    final int hash;    //节点的hash值    final K key;        //存入的key值    V value;            //寄存的值    Node<K,V> next;    //下一个节点....}

留神hashMap和LinkedList的区别,后者是双向的,而hashMap中的Node是单向的。

5.get()操作

public V get(Object key) {    Node<K,V> e;    return (e = getNode(hash(key), key)) == null ? null : e.value; //代码内容在这里}

调用getNode()办法,计算hash(key)值,通过Hash来取得node

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) {        //第一个 检测数组中的hash定位取得第一个Node        if (first.hash == hash &&((k = first.key) == key || (key != null && key.equals(k))))            return first;        //第一个不是,那么就是后续节点中,可能是链表模式,可能是红黑树        if ((e = first.next) != null) {            if (first instanceof TreeNode) //如果是红黑树,通过getTreeNode()办法取得                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;}

链表模式的获取比较简单,红黑树的取得,咱们放在上面红黑树独自进行介绍。

三,TreeMap

TreeMap和之前的两个map就不同了,它没有应用哈希表,而是间接应用红黑树解决,它的字段只保留了根节点

private final Comparator<? super K> comparator; //排序比拟器private transient Entry<K,V> root;    //根节点private transient int size = 0;private transient int modCount = 0;

1.get()

public V get(Object key) {    Entry<K,V> p = getEntry(key);    return (p==null ? null : p.value);}

getEntry()

final Entry<K,V> getEntry(Object key) {    // Offload comparator-based version for sake of performance    if (comparator != null)        return getEntryUsingComparator(key);    if (key == null)        throw new NullPointerException();    @SuppressWarnings("unchecked")        Comparable<? super K> k = (Comparable<? super K>) key;    Entry<K,V> p = root;    while (p != null) {        int cmp = k.compareTo(p.key);        //左右分流        if (cmp < 0)            p = p.left;        else if (cmp > 0)            p = p.right;        else            return p;    }    return null;}

2.put() 波及红黑树的操作,所以代码比拟长

public V put(K key, V value) {    Entry<K,V> t = root;    if (t == null) {        compare(key, key); // type (and possibly null) check         root = new Entry<>(key, value, null);        size = 1;        modCount++;        return null;    }    int cmp;    Entry<K,V> parent;    // split comparator and comparable paths    Comparator<? super K> cpr = comparator;    if (cpr != null) {        do {            parent = t;            cmp = cpr.compare(key, t.key);            if (cmp < 0)                t = t.left;            else if (cmp > 0)                t = t.right;            else                return t.setValue(value);        } while (t != null);    }    else {        if (key == null)            throw new NullPointerException();        @SuppressWarnings("unchecked")            Comparable<? super K> k = (Comparable<? super K>) key;        do {            parent = t;            cmp = k.compareTo(t.key);            if (cmp < 0)                t = t.left;            else if (cmp > 0)                t = t.right;            else                return t.setValue(value);        } while (t != null);    }    Entry<K,V> e = new Entry<>(key, value, parent);    if (cmp < 0)        parent.left = e;    else        parent.right = e;    fixAfterInsertion(e);    size++;    modCount++;    return null;}

3.remove()

public V remove(Object key) {    Entry<K,V> p = getEntry(key);    if (p == null)        return null;     V oldValue = p.value;    deleteEntry(p);  //理论办法    return oldValue;}

总结:

HashMap体现得更像TreeMap和HashTable的结合体。

最初

感激你看到这里,看完有什么的不懂的能够在评论区问我,感觉文章对你有帮忙的话记得给我点个赞,每天都会分享java相干技术文章或行业资讯,欢送大家关注和转发文章!