Java集合容器系列06-HashTable

HashTable详解

一、HashTable介绍

    与HashMap一样HashTable也是散列表的一个实现,他也用于存储键值映射。HashTable容器中存储的key和value均不能为null。为了成功检索和存储键值对映射,HashTable中的所有键值对中的key都必须实现hashCode和equals方法。影响HashTable性能的因素有两个:初始容量(initialCapcity)和加载因子(loadFactor)加载因子是HashTable中槽的个数或者说采用拉链法实现的Entry数组table的长度,初始容量在HashTable创建时指定,当发生Hash冲突时一个槽可能存储多个键值对节点,加载因子用于表示容器的填充程度,关于何时进行rehash不绝对依赖于初始容量和加载因子,这二者只是提供建议具体依赖于方法实现。通常默认的加载因子是0.75这是为了在时间和空间上寻求平衡,加载因子过高虽然一方面降低了空间浪费但是查找某个节点的成本就提高,反之加载因子过低会浪费大量数组空间。HashTable是线程安全的。如果在HashTable的迭代器Iterator创建之后对它进行结构性修改,他会立即抛出ConcurrentModificationException异常。

    在日常开发中如果不需要线程安全实现建议使用HashMap类,如果需要高并发实现建议使用ConcurrentHashMap替代HashTable。

二、HashTable数据结构

1 - 继承结构
public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable

    HashTable继承自Dictionary,实现了Map、Cloneable、java.io.Serializable接口。

    Dictionary定义与AbstractMap不同的操作键值对的接口,HashTable实现了Dictionary遍历键值对集合是基于Enumeration而不是迭代器Iterator这点区别于HashMap。

2 - 成员变量
    //保存key/value键值对的数组,采用拉链表实现,每个Entry对象实际是一个单向链表
    private transient Entry<?,?>[] table;
    
    //实际存储的键值对个数
    private transient int count;

    //阀值,超过这个阀值底层数组table会进行扩容
    private int threshold;

    //加载因子
    private float loadFactor;

    //结构修改次数
    private transient int modCount = 0;

    //Hashtable的key集合用set存储意味着没有重复的元素
    private transient volatile Set<K> keySet;
    //Hashtable中存储Key/value集合,用Set存储不包含重复元素
    private transient volatile Set<Map.Entry<K,V>> entrySet;
    //Hashtable存储值value,使用集合结构Collection存储,可以存储重复元素
    private transient volatile Collection<V> values;

三、HashTable源码分析

1 - 构造函数
   //构造函数,指定了容器的初始化容量和加载因子,基于二者计算阀值thredshold
   public Hashtable(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);

        if (initialCapacity==0)
            initialCapacity = 1;
        this.loadFactor = loadFactor;
        table = new Entry<?,?>[initialCapacity];
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }

    //构造函数,容器初始化容量设置为方法指定容量,加载因子默认0.75
    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

    //无参构造函数,容器初始化容量默认为11,加载因子默认为0.75
    public Hashtable() {
        this(11, 0.75f);
    }

    //构造函数,入参是一个Map对象,初始化容量取11和集合键值对节点个数n*2的最大值,默认加载因子0.75
    //将容器中保存的所有键值对插入Hashtable中
    public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }
2 - V get(Object key)方法 - 获取key对应value
    public synchronized V get(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }
        return null;
    }

    Hashtable容器根据key获取value方法的基本逻辑:首先调用对象key的hashCode方法获取key的hashCode与指定16位数值Ox7FFFFFFF做&运算,然后将计算后的结果对数组table长度取模,获取key在Entry数组table中的位置,也就是说第一步其实是根据key的hash值确定键值对节点在数组table中的下标;然后我们知道Entry数组table其实是采用拉链法获取到数组项Entry对象其实是一条单向链表,接下来就是遍历这个链表获取匹配指定key的键值对,如果遍历结束查询不到则返回null。

 

3 - V put(K key, V value) - 插入键值对
    public synchronized V put(K key, V value) {
        //value的空对象校验
        if (value == null) {
            throw new NullPointerException();
        }

        Entry<?,?> tab[] = table;
        //获取key的hashCode
        int hash = key.hashCode();
        //确定插入Entry数组的下标位置,即确定所属单链表
        int index = (hash & 0x7FFFFFFF) % tab.length;
        //获取对应Entry对象即单链表
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        //遍历链表,若对应key在链表中已存在,直接重置匹配键值对节点的value值为方法参数指定值value,结束返回节点旧值
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }
        //容器中不存在key对应的键值对节点,则插入指定键值对
        addEntry(hash, key, value, index);
        return null;
    }

    根据源码可知Hashtable调用put方法插入键值对的主要流程逻辑如下:

1)value是否为null判断;

2)基于key的hashCode和Hashtable的容量计算新插入键值对在Entry数组table的下标位置;

3)如果数组table对应位置存在Entry对象(实质是一条单向链表),遍历保存在Entry对象上的链表,若存在匹配key的键值对节点直接重置该键值对节点对应的值为指定value,结束返回键值对节点的旧值;

4)若Hashtable不存在key对应的节点信息,则调用addEntry(hash,key, value, index)方法,我们进入该方法源码看他做了什么

    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;
        }

        // Creates the new entry.
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

    该方法主要做的事首先是因为调用该方法会在容器中插入一个键值对节点发生了结构性修改,因此容器的结构修改计数器递增,这个变量在所有的容器类中作用基本一致:是为了实现容器类fail-fast错误检测机制,在容器进行迭代遍历的时候检测此时容器是否发生结构性修改,如果发生了结构性修改立即抛出ConcurrentException异常;接下来判断容器保存的键值对节点个数是否超过阀值,超过则调用rehash方法重新调整数组table长度,并根据key重新计算插入键值对节点对应的table数组下标位置;最后根据数组下标位置获取Entry对象(即保存hash冲突节点的单链表),在链表头部插入新的键值对节点。

 

3 - void rehash()方法 - 调整容器容量,将容器中保存的键值对填充到新的Entry数组table中

    protected void rehash() {
        //获取旧数组table的长度
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        //计算新数组长度
        int newCapacity = (oldCapacity << 1) + 1;
        
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            //旧table数组长度达到最大限制则不进行扩容直接返回
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            //计算的扩容长度大于最大限制则重新指定扩容后的长度为最大数组长度限制
            newCapacity = MAX_ARRAY_SIZE;
        }
        //创建新Entry数组
        Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
        //结构性修改计数器加1
        modCount++;
        //重新计算阀值
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        //数组引用指向新数组
        table = newMap;
        //遍历旧Entry数组table所有数组项上的单链表,将保存的键值对数据插入到新table数组的新位置
        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;

                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }

    当Hashtable容器中保存的键值对节点个数超出阀值threadshold时为了平衡空间和Hashtable键值对的查询速度会调用rehash方法对容器进行扩容。方法的主要流程逻辑如下:

1)首先根据一般情况下的扩容公式将扩容后的容量设置为原来的容量n*2+1,并基于Hashtable类内部数组的最大长度限制进行重新调整,超出最大扩容限制不进行扩容;

2)基于调整后的容量创建对应长度的新table数组,并让内部数组引用指向新数组;

3)将原table数组保存的所有键值对数据插入到新table数组的新位置;

 

4 - Enumeration<K> keys()方法 - 返回可用于遍历key集合的Enumeration对象
    public synchronized Enumeration<K> keys() {
        return this.<K>getEnumeration(KEYS);
    }

    为了比较Hashtable基于Enumeration与HashMap基于迭代器Iterator遍历键值对节点的不同点,我们选择了Hashtable的keys方法展开分析,方法内部直接调用getEnumeration方法,我们继续进入该方法源码:

    private <T> Enumeration<T> getEnumeration(int type) {
        if (count == 0) {
            return Collections.emptyEnumeration();
        } else {
            return new Enumerator<>(type, false);
        }
    }

    首先容器中的元素若为空方法直接返回一个Collections中缓存的Enumeration空对象,否则调用Enumerator类的构造函数,我们看下该类的源码

    private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
        //指向Hashtable中保存键值对节点的数组
        Entry<?,?>[] table = Hashtable.this.table;
        //容器保存的键值对节点个数
        int index = table.length;
        Entry<?,?> entry;
        //迭代器最后一次访问的节点
        Entry<?,?> lastReturned;
        //指定迭代访问的是key还是value
        int type;
        //设置节点是否可删除
        boolean iterator;


        protected int expectedModCount = modCount;

        Enumerator(int type, boolean iterator) {
            this.type = type;
            this.iterator = iterator;
        }

        public boolean hasMoreElements() {
            Entry<?,?> e = entry;
            int i = index;
            Entry<?,?>[] t = table;
            /* Use locals for faster loop iteration */
            while (e == null && i > 0) {
                e = t[--i];
            }
            entry = e;
            index = i;
            return e != null;
        }

        @SuppressWarnings("unchecked")
        public T nextElement() {
            Entry<?,?> et = entry;
            int i = index;
            Entry<?,?>[] t = table;
            /* Use locals for faster loop iteration */
            while (et == null && i > 0) {
                et = t[--i];
            }
            entry = et;
            index = i;
            if (et != null) {
                Entry<?,?> e = lastReturned = entry;
                entry = e.next;
                return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
            }
            throw new NoSuchElementException("Hashtable Enumerator");
        }

        // Iterator methods
        public boolean hasNext() {
            return hasMoreElements();
        }

        public T next() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            return nextElement();
        }

        public void remove() {
            if (!iterator)
                throw new UnsupportedOperationException();
            if (lastReturned == null)
                throw new IllegalStateException("Hashtable Enumerator");
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();

            synchronized(Hashtable.this) {
                Entry<?,?>[] tab = Hashtable.this.table;
                int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;

                @SuppressWarnings("unchecked")
                Entry<K,V> e = (Entry<K,V>)tab[index];
                for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
                    if (e == lastReturned) {
                        modCount++;
                        expectedModCount++;
                        if (prev == null)
                            tab[index] = e.next;
                        else
                            prev.next = e.next;
                        count--;
                        lastReturned = null;
                        return;
                    }
                }
                throw new ConcurrentModificationException();
            }
        }
    }

    Hashtable对键值对节点中的key或者value进行迭代访问内部其实就是基于Enumerator类,它实现了Enumeration和Iterator接口。我们分析下该类的nextElement方法了解下Hashtable中键值对的迭代访问顺序。由方法源码可知它是通过type值确定访问的是key、value还是键值对节点,访问顺序是基于内部数组table下标逆序访问,然后按顺序遍历数组项上保存的hash冲突键值对节点的单向链表,因为hash冲突链每次发生hash冲突都是在头部插入,因此遍历顺序正好和链表上键值对插入顺序相反。总而言之,也就是说Hashtable中键值对是无序的,容器中键值对的访问顺序受key的hash值和插入时机两个因素的影响。

5 - 其他成员方法
    //获取容器保存的键值对节点个数
    public synchronized int size() {
        return count;
    }

    //Hashtable容器对象是否为空判断
    public synchronized boolean isEmpty() {
        return count == 0;
    }

    //返回容器中所有键值对的key集合Enumeration,可通过它遍历所有key
    public synchronized Enumeration<K> keys() {
        return this.<K>getEnumeration(KEYS);
    }

    //返回容器所有键值对的value集合,可通过它访问所有value
    public synchronized Enumeration<V> elements() {
        return this.<V>getEnumeration(VALUES);
    }

    //判断容器中是否存在value值等于方法指定value的键值对
    public synchronized boolean contains(Object value) {
        if (value == null) {
            throw new NullPointerException();
        }

        Entry<?,?> tab[] = table;
        for (int i = tab.length ; i-- > 0 ;) {
            for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
                if (e.value.equals(value)) {
                    return true;
                }
            }
        }
        return false;
    }


    public boolean containsValue(Object value) {
        return contains(value);
    }

    //容器是否包含指定key的键值对
    public synchronized boolean containsKey(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return true;
            }
        }
        return false;
    }
    
    //删除容器中key对应键值对节点
    public synchronized V remove(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                modCount++;
                if (prev != null) {
                    prev.next = e.next;
                } else {
                    tab[index] = e.next;
                }
                count--;
                V oldValue = e.value;
                e.value = null;
                return oldValue;
            }
        }
        return null;
    }

    //插入所有Map对象t中所有键值对节点
    public synchronized void putAll(Map<? extends K, ? extends V> t) {
        for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
            put(e.getKey(), e.getValue());
    }

    //清空容器,将table数组上保存的对象清空
    public synchronized void clear() {
        Entry<?,?> tab[] = table;
        modCount++;
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;
        count = 0;
    }

    //拷贝函数
    public synchronized Object clone() {
        try {
            Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
            t.table = new Entry<?,?>[table.length];
            for (int i = table.length ; i-- > 0 ; ) {
                t.table[i] = (table[i] != null)
                    ? (Entry<?,?>) table[i].clone() : null;
            }
            t.keySet = null;
            t.entrySet = null;
            t.values = null;
            t.modCount = 0;
            return t;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

    //返回Hashtable的字符串显示格式
    public synchronized String toString() {
        int max = size() - 1;
        if (max == -1)
            return "{}";

        StringBuilder sb = new StringBuilder();
        Iterator<Map.Entry<K,V>> it = entrySet().iterator();

        sb.append('{');
        for (int i = 0; ; i++) {
            Map.Entry<K,V> e = it.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key.toString());
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value.toString());

            if (i == max)
                return sb.append('}').toString();
            sb.append(", ");
        }
    }
    
    //获取容器中所有键值对节点的key集合
    public Set<K> keySet() {
        if (keySet == null)
            keySet = Collections.synchronizedSet(new KeySet(), this);
        return keySet;
    }
    
    //返回容器中键值对节点Entry集合
    public Set<Map.Entry<K,V>> entrySet() {
        if (entrySet==null)
            entrySet = Collections.synchronizedSet(new EntrySet(), this);
        return entrySet;
    }
    //返回容器中所有键值对的value集合
    public Collection<V> values() {
        if (values==null)
            values = Collections.synchronizedCollection(new ValueCollection(),
                                                        this);
        return values;
    }

    //比较当前对象和指定对象是否逻辑相等
    public synchronized boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map))
            return false;
        Map<?,?> t = (Map<?,?>) o;
        if (t.size() != size())
            return false;

        try {
            Iterator<Map.Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()) {
                Map.Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    if (!(t.get(key)==null && t.containsKey(key)))
                        return false;
                } else {
                    if (!value.equals(t.get(key)))
                        return false;
                }
            }
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }

        return true;
    }

    //返回Hashtable对象的hash值
    public synchronized int hashCode() {
        int h = 0;
        if (count == 0 || loadFactor < 0)
            return h;  // Returns zero

        loadFactor = -loadFactor;  // Mark hashCode computation in progress
        Entry<?,?>[] tab = table;
        for (Entry<?,?> entry : tab) {
            while (entry != null) {
                h += entry.hashCode();
                entry = entry.next;
            }
        }

        loadFactor = -loadFactor;  // Mark hashCode computation complete

        return h;
    }

    //获取key对应的value若容器中不存在该键值对则返回指定的默认值defaultValue
    @Override
    public synchronized V getOrDefault(Object key, V defaultValue) {
        V result = get(key);
        return (null == result) ? defaultValue : result;
    }

    //遍历Hashtable容器所有键值对对每个节点apply函数action,如果在遍历过程出现了结构性修改则立即抛出异常
    @SuppressWarnings("unchecked")
    @Override
    public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);    
                                            
        final int expectedModCount = modCount;

        Entry<?, ?>[] tab = table;
        for (Entry<?, ?> entry : tab) {
            while (entry != null) {
                action.accept((K)entry.key, (V)entry.value);
                entry = entry.next;

                if (expectedModCount != modCount) {
                    throw new ConcurrentModificationException();
                }
            }
        }
    }

    //遍历Hashtable所有键值对,对每个键值对节点apply函数action,将action返回结果作为对应节点的新value
    @SuppressWarnings("unchecked")
    @Override
    public synchronized void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);     // explicit check required in case
                                              // table is empty.
        final int expectedModCount = modCount;

        Entry<K, V>[] tab = (Entry<K, V>[])table;
        for (Entry<K, V> entry : tab) {
            while (entry != null) {
                entry.value = Objects.requireNonNull(
                    function.apply(entry.key, entry.value));
                entry = entry.next;

                if (expectedModCount != modCount) {
                    throw new ConcurrentModificationException();
                }
            }
        }
    }

    //与put方法貌似实现逻辑一致,实现的效果与命名不一致,方法名表达的意思应该是如果不存在则进行设置否则不操作
    @Override
    public synchronized V putIfAbsent(K key, V value) {
        Objects.requireNonNull(value);

        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;
                if (old == null) {
                    entry.value = value;
                }
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

    //删除容器中保存的键值对节点,只有当容器节点的key与value都与方法指定key、value逻辑相等时才会进行删除
    @Override
    public synchronized boolean remove(Object key, Object value) {
        Objects.requireNonNull(value);

        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
            if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
                modCount++;
                if (prev != null) {
                    prev.next = e.next;
                } else {
                    tab[index] = e.next;
                }
                count--;
                e.value = null;
                return true;
            }
        }
        return false;
    }

    //替换容器中指定key对应的键值对节点的value,当存在key对应键值对且value等于oldValue时才会将键值对对应value设 
    //置为指定value,否则操作失败
    @Override
    public synchronized boolean replace(K key, V oldValue, V newValue) {
        Objects.requireNonNull(oldValue);
        Objects.requireNonNull(newValue);
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for (; e != null; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                if (e.value.equals(oldValue)) {
                    e.value = newValue;
                    return true;
                } else {
                    return false;
                }
            }
        }
        return false;
    }

    //替换容器中key对应键值对节点的value为指定value,成功则返回节点旧value
    @Override
    public synchronized V replace(K key, V value) {
        Objects.requireNonNull(value);
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for (; e != null; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }
        return null;
    }

    //如果容器中存在key对应的键值对节点那么方法结束返回key对应的value,否则应用指定映射函数为key生成value,如果生 
    //成的value不为空,那么为指定key和生成的value创建新的键值对节点插入容器中
    @Override
    public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);

        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for (; e != null; e = e.next) {
            if (e.hash == hash && e.key.equals(key)) {
                // Hashtable not accept null value
                return e.value;
            }
        }

        V newValue = mappingFunction.apply(key);
        if (newValue != null) {
            addEntry(hash, key, newValue, index);
        }

        return newValue;
    }

    //基于key和指定映射函数remappingFunction,如果容器中存在key对应的键值对,那么传入key apply映射函数 
    //remappingFunction,如果返回为null则删除该键值对节点返回null,否则使用返回结果覆盖原来键值对的value值
    //返回新插入的value
    @Override
    public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);

        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
            if (e.hash == hash && e.key.equals(key)) {
                V newValue = remappingFunction.apply(key, e.value);
                if (newValue == null) {
                    modCount++;
                    if (prev != null) {
                        prev.next = e.next;
                    } else {
                        tab[index] = e.next;
                    }
                    count--;
                } else {
                    e.value = newValue;
                }
                return newValue;
            }
        }
        return null;
    }

    //与上述方法作用类似,区别在于当容器不存在key对应的键值对节点时,本方法会基于指定映射函数remappingFunction
    //产生新的vlaue,如果映射函数返回结果不为空则将产生的value和指定key作为新的键值对插入容器中
    @Override
    public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);

        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
            if (e.hash == hash && Objects.equals(e.key, key)) {
                V newValue = remappingFunction.apply(key, e.value);
                if (newValue == null) {
                    modCount++;
                    if (prev != null) {
                        prev.next = e.next;
                    } else {
                        tab[index] = e.next;
                    }
                    count--;
                } else {
                    e.value = newValue;
                }
                return newValue;
            }
        }

        V newValue = remappingFunction.apply(key, null);
        if (newValue != null) {
            addEntry(hash, key, newValue, index);
        }

        return newValue;
    }

    //与上面两个方法功能类似,区别在于若容器中不存在key对应的键值对节点,它既不像第一个方法直接返回null,也不想第二 
    //个方法apply映射函数,将返回结果做为新插入键值对节点的value,而是直接取方法指定的key,vlaue插入到容器中
    @Override
    public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);

        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
            if (e.hash == hash && e.key.equals(key)) {
                V newValue = remappingFunction.apply(e.value, value);
                if (newValue == null) {
                    modCount++;
                    if (prev != null) {
                        prev.next = e.next;
                    } else {
                        tab[index] = e.next;
                    }
                    count--;
                } else {
                    e.value = newValue;
                }
                return newValue;
            }
        }

        if (value != null) {
            addEntry(hash, key, value, index);
        }

        return value;
    }

 

转载于:https://my.oschina.net/zhangyq1991/blog/1923068

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值