zoukankan      html  css  js  c++  java
  • (原)HashMap之java8新特性

    首先说一下HashMap存储结构,数组、链表、树这三种数据结构形成了hashMap。
    存储结构下图所示,根据key的hash与table长度确定table位置,同一个位置的key以链表形式存储,超过一定限制链表转为树。
    数组的具体存取规则是tab[(n-1) & hash],其中tab为node数组,n为数组的长度,hash为key的hash值。

    //链表中数据的临界值,如果达到8,就进行resize扩展,如果数组大于64则转换为树.

    static  final  int TREEIFY_THRESHOLD = 8;
    

    //如果链表的数据小于6,则从树转换为链表.

    static  final  int UNTREEIFY_THRESHOLD = 6;
    

    //如果数组的size大于64,则把链表进行转化为树

    static  final  int MIN_TREEIFY_CAPACITY = 64
    

    hashMap.png

    //根据key匹配Node,如果匹配不到key,则返回defaultValue

    @Override
    public V getOrDefault(Object key, V defaultValue) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
    }
    

    //根据key匹配Node,如果匹配不到则增加key-value,返回null,如果匹配到Node,如果oldValue不等于null则不进行value覆盖,返回oldValue

    @Override
    public V putIfAbsent(K key, V value) {
        return putVal(hash(key), key, value, true, true);
    }
    

    //根据key匹配node,如果value也相同则删除

    @Override
    public boolean remove(Object key, Object value) {
        return removeNode(hash(key), key, value, true, true) != null;
    }
    

    //根据key匹配node,如果value也相同则使用newValue覆盖返回true,否则返回false

    @Override
    public boolean replace(K key, V oldValue, V newValue) {
        Node<K,V> e; V v;
        if ((e = getNode(hash(key), key)) != null &&
           ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
           e.value = newValue;
           afterNodeAccess(e);
           return true;
        }
        return false;
    }
    

    //根据key做匹配Node,(匹配不到则新建然后重排)如果Node有value,则直接返回oldValue,如果没有value则根据Function接口的apply方法获取value,返回value。
    //Function接口的apply的入参为key,调用computeIfAbsent时重写Function接口可以根据key进行逻辑处理,apply的返回值即为要存储的value。

    @Override
    public V computeIfAbsent(K key,
                      Functionsuper K, ? extends V> mappingFunction) {
        if (mappingFunction == null)
           throw new NullPointerException();
        int hash = hash(key);
        Node<K,V>[] tab; Node<K,V> first; int n, i;
        int binCount = 0;
        TreeNode<K,V> t = null;
        Node<K,V> old = null;
        if (size > threshold || (tab = table) == null ||
           (n = tab.length) == 0)
           n = (tab = resize()).length;
        if ((first = tab[i = (n - 1) & hash]) != null) {
           //如果已经转为树,按照树的规则进行处理
           if (first instanceof TreeNode)
             old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
           else {
             Node<K,V> e = first; K k;
             //查找整个链表,找到对应的key
             do {
              if (e.hash == hash &&
                  ((k = e.key) == key || (key != null && key.equals(k)))) {
                  old = e;
                  break;
              }
              ++binCount;
             } while ((e = e.next) != null);
           }
           V oldValue;
           if (old != null && (oldValue = old.value) != null) {
             afterNodeAccess(old);
             return oldValue;
           }
        }
        //根据重写逻辑计算返回value
        V v = mappingFunction.apply(key);
        if (v == null) {
           return null;
        } else if (old != null) {
           old.value = v;
           afterNodeAccess(old);
           return v;
        }
        else if (t != null)
           t.putTreeVal(this, tab, hash, key, v);
        else {
           //如果匹配不到则table加入数据
           tab[i] = newNode(hash, key, v, first);
           if (binCount >= TREEIFY_THRESHOLD - 1)
             treeifyBin(tab, hash);
        }
        ++modCount;
        ++size;
        afterNodeInsertion(true);
        return v;
    }
    

    //V computeIfPresent(K key,BiFunction remappingFunction):根据key做匹配,如果匹配不上则返回null,匹配上根据BiFunction的apply方法获取value,返回value。BiFunction接口的apply的入参为key、oldValue,调用computeIfPresent时重写Function接口可以根据key和oldValue进行逻辑处理,apply的返回值如果为null则删除该节点,否则即为要存储的value。

    public V computeIfPresent(K key,
                       BiFunctionsuper K, ? super V, ? extends V> remappingFunction) {
        if (remappingFunction == null)
           throw new NullPointerException();
        Node<K,V> e; V oldValue;
        int hash = hash(key);
        if ((e = getNode(hash, key)) != null &&
           (oldValue = e.value) != null) {
           //使用key和原value作为入参
           V v = remappingFunction.apply(key, oldValue);
           if (v != null) {
             e.value = v;
             afterNodeAccess(e);
             return v;
           }
           else
      removeNode(hash, key, null, false, true);
        }
        return null;
    }   
    

    //V compute(K key,BiFunction remappingFunction):根据key做匹配,根据BiFunction的apply返回做存储的value。匹配到Node做value替换,匹配不到新增node。apply的返回值如果为null则删除该节点,否则即为要存储的value。

    @Override
    public V compute(K key,
               BiFunctionsuper K, ? super V, ? extends V> remappingFunction) {
        if (remappingFunction == null)
           throw new NullPointerException();
        int hash = hash(key);
        Node<K,V>[] tab; Node<K,V> first; int n, i;
        int binCount = 0;
        TreeNode<K,V> t = null;
        Node<K,V> old = null;
        if (size > threshold || (tab = table) == null ||
           (n = tab.length) == 0)
           n = (tab = resize()).length;
        if ((first = tab[i = (n - 1) & hash]) != null) {
           if (first instanceof TreeNode)
             old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
           else {
             Node<K,V> e = first; K k;
             do {
              if (e.hash == hash &&
                  ((k = e.key) == key || (key != null && key.equals(k)))) {
                  old = e;
                  break;
              }
              ++binCount;
             } while ((e = e.next) != null);
           }
        }
        V oldValue = (old == null) ? null : old.value;
        //使用key和原value作为入参
        V v = remappingFunction.apply(key, oldValue);
        if (old != null) {
           if (v != null) {
             old.value = v;
             afterNodeAccess(old);
           }
           else
      removeNode(hash, key, null, false, true);
        }
        else if (v != null) {
           if (t != null)
             t.putTreeVal(this, tab, hash, key, v);
           else {
             tab[i] = newNode(hash, key, v, first);
             if (binCount >= TREEIFY_THRESHOLD - 1)
              treeifyBin(tab, hash);
           }
           ++modCount;
           ++size;
           afterNodeInsertion(true);
        }
        return v;
    }
    

    // V merge(K key, V value,BiFunction remappingFunction):功能大部分与compute相同,不同之处在于BiFunction中apply的参数,入参为oldValue、value,调用merge时根据两个value进行逻辑处理并返回value。

    @Override
    public V merge(K key, V value,
                BiFunctionsuper V, ? super V, ? extends V> remappingFunction) {
        if (value == null)
           throw new NullPointerException();
        if (remappingFunction == null)
           throw new NullPointerException();
        int hash = hash(key);
        Node<K,V>[] tab; Node<K,V> first; int n, i;
        int binCount = 0;
        TreeNode<K,V> t = null;
        Node<K,V> old = null;
        if (size > threshold || (tab = table) == null ||
           (n = tab.length) == 0)
           n = (tab = resize()).length;
        if ((first = tab[i = (n - 1) & hash]) != null) {
           if (first instanceof TreeNode)
             old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
           else {
             Node<K,V> e = first; K k;
             do {
              if (e.hash == hash &&
                  ((k = e.key) == key || (key != null && key.equals(k)))) {
                  old = e;
                  break;
              }
              ++binCount;
             } while ((e = e.next) != null);
           }
        }
        if (old != null) {
           V v;
           if (old.value != null)
             //使用新老value作为入参
             v = remappingFunction.apply(old.value, value);
           else
             v = value;
           if (v != null) {
             old.value = v;
             afterNodeAccess(old);
           }
           else
             removeNode(hash, key, null, false, true);
           return v;
        }
        if (value != null) {
           if (t != null)
             t.putTreeVal(this, tab, hash, key, value);
           else {
             tab[i] = newNode(hash, key, value, first);
             if (binCount >= TREEIFY_THRESHOLD - 1)
              treeifyBin(tab, hash);
           }
           ++modCount;
           ++size;
           afterNodeInsertion(true);
        }
        return value;
    }
    

    // void forEach(BiConsumer action):调用此方法时实现BiConsumer接口重写void accept(Object o, Object o2)方法,其中o为key,o2为value,可根据自己的实现对map中所有数据进行处理。

    @Override
    public void forEach(BiConsumersuper K, ? super V> action) {
        Node<K,V>[] tab;
        if (action == null)
           throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
           int mc = modCount;
           for (int i = 0; i < tab.length; ++i) {
             for (Node<K,V> e = tab[i]; e != null; e = e.next)
              action.accept(e.key, e.value);
           }
           if (modCount != mc)
             throw new ConcurrentModificationException();
        }
    }
    

    // void replaceAll(BiFunction function):调用此方法时重写BiFunction的Object apply(Object o, Object o2)方法,其中o为key,o2为value,根据重写方法逻辑进行重新赋值。

    @Override
    public void replaceAll(BiFunctionsuper K, ? super V, ? extends V> function) {
        Node<K,V>[] tab;
        if (function == null)
           throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
           int mc = modCount;
           for (int i = 0; i < tab.length; ++i) {
             for (Node<K,V> e = tab[i]; e != null; e = e.next) {
              e.value = function.apply(e.key, e.value);
             }
           }
           if (modCount != mc)
             throw new ConcurrentModificationException();
        }
    }
    

    computeIfAbsent、computeIfPresent、compute对比

    computeIfAbsent:如果key已存在,返回oldVlaue;不存在创建,返回新创建value

    computeIfPresent:如果key不存在,返回null;如果已存在,value为null则删除此节点,不为null替换节点value并返回此value。

    compute:如果key不存在,新建key进行存储;如果key存在,value为null则删除此节点,不为null替换节点value并返回此value。

  • 相关阅读:
    小任务之使用SVG画柱状图~
    webpack.config.js配置遇到Error: Cannot find module '@babel/core'问题
    今日笔记
    querySelector与getElementBy等的区别
    JavaScript实现邮箱后缀提示功能
    JavaScript中按键事件的e.keyCode || e.which || e.charCode
    跳转页面&回到上一页
    echarts.js 做图表的插件
    局域网内电脑远程访问共享
    同引擎mysql数据库转导快
  • 原文地址:https://www.cnblogs.com/shengkejava/p/6771469.html
Copyright © 2011-2022 走看看