zoukankan      html  css  js  c++  java
  • 作业13:Map相关知识点(一)

    一 Map相关类图

    二 Map接口

    1 Map接口中的方法

    jdk 方法名 简单描述
    put(K,V):V 添加value,当Key对应无值,返回null;有值则返回上一个值。(覆盖式,可以反复覆盖前一个value)
    8 putIfAbsent(K,V):V 添加value,当Key对应无值,返回null;有值则返回上一个值。(独占式,只能赋值一次(除非调用了删除))
    get(Object):V 获取key对应的value
    8 getOrDefault(Object,V):V 获取key对应的value,当获取不到时,返回传入的V值。
    remove(Object):V 删除key对应的value
    8 remove(Object,Object):boolean 当传入的value与map中存的value相等,则执行删除操作
    keySet():Set<K> 返回所有key值
    values():Collection<V> 返回所有value值
    entrySet():Set<Entry<K,V>> 返回所有Entry值(下一节介绍)
    containsValue(Object):boolean 判断是否包含该value
    containsKey(Object):boolean 判断是否包含该value
    8 replace(K,V):V 当该key对应有值,将value覆盖;否则,不操作。
    8 replace(K,V value1,V value2):boolean 当传入的value1与key对应值相等,将value2覆盖value1。
    8 merge(K,V,BiFunction):V 如果指定的键尚未与值关联或与 null 关联, 则将其与给定的非 null 值相关联。
    8 compute(K,BiFunction):V 与put相同,直接覆盖
    8 computeIfAbsent(K,Function):V 如果指定的键尚未与值关联或与 null 关联,使用计算值替换。
    8 computeIfPresent(K,BiFunction):V 如果指定的键已有绑定值则采用计算值替换
    8 forEach 遍历

    2、Demo

    • get和getOrDefault
    Map<Integer, String> map = new HashMap<>();
    System.out.println(map.get(1)); // null
    System.out.println(map.getOrDefault(1,"1")); // "1"
    
    • put和putIfAbsent
    // put
    Map<Integer, String> map = new HashMap<>();
    System.out.println(map.put(1, "1")); // null
    System.out.println(map.put(1, "2")); // 1
    System.out.println("map:"+map); // map:{1=2}
    
    // putIfAbsent
    Map<Integer, String> map = new HashMap<>();
    System.out.println(map.putIfAbsent(1, "1")); // null
    System.out.println(map.putIfAbsent(1, "2")); // 1
    System.out.println("map:"+map); // map:{1=1}
    
    • remove
    Map<Integer, String> map = new HashMap<>();
    map.put(1,"1");
    System.out.println(map.remove(1,"2")); // false
    System.out.println(map); // {1=1}
    System.out.println(map.remove(1)); // 1
    System.out.println(map); // {}
    
    • merge
    Map<Integer, String> map = new HashMap<>();
    map.put(1,"1");
    map.put(2,"2");
    map.merge(1,"test",String::concat); // 1取到“1”,“1”.concat("test") = "1test"
    map.merge(3,"3",String::concat); // 3对应null,直接返回“3”
    System.out.println("map:"+map); // map:{1=1test, 2=2, 3=3}
    
    // merge最后一个参数是BiFunction,下面三种写法结果相同
    // BiFunction是二元函数,传入两个参数,返回一个结果。
    map.merge(1, "test", String::concat);
    map.merge(1, "test", (s, s2) -> s.concat(s2));
    map.merge(1, "test", new BiFunction<String, String, String>() {
        @Override
        public String apply(String oldVal, String newVal) {
            return oldVal.concat(newVal);
        }
    });
    

    3 jdk部分源码

    public interface Map<K, V> {
        interface Entry<K, V> {
    		K getKey();
            V getValue();
            V setValue(V value);
            // ......
        }
        
        default V getOrDefault(Object key, V defaultValue) {
            V v;
            return (((v = get(key)) != null) || containsKey(key))
                ? v
                : defaultValue;
        }
        
        default V putIfAbsent(K key, V value) {
            V v = get(key);
            if (v == null) {
                v = put(key, value);
            }
            return v;
        }
        
        default boolean remove(Object key, Object value) {
            Object curValue = get(key);
            if (!Objects.equals(curValue, value) ||
                (curValue == null && !containsKey(key))) {
                return false;
            }
            remove(key);
            return true;
        }
        
        default V computeIfAbsent(K key,
                Function<? super K, ? extends V> mappingFunction) {
            Objects.requireNonNull(mappingFunction);
            V v;
            if ((v = get(key)) == null) {
                V newValue;
                if ((newValue = mappingFunction.apply(key)) != null) {
                    put(key, newValue);
                    return newValue;
                }
            }
            return v;
        }
       
       // ... 都是比较简单的代码,不做过多介绍了
       // Java8的java.util.function包可以参考:https://www.cnblogs.com/linzhanfly/p/9686941.html
    }
    

    三 SortedMap(继承Map接口)

    1 SortedMap接口中的方法

    方法 描述
    comparator():Comparator< ? super K > 返回比较器(可能返回null)
    subMap(K,K):SortedMap<K,V> 截取fromKey到toKey的Map
    headMap(K):SortedMap<K,V> 截取小于toKey的Map
    tailMap(K):SortedMap<K,V> 截取大于fromKey的Map
    fristKey():K 最小的Key值
    lastKey():K 最大的Key值

    2 Demo

    • keySet和values
    SortedMap<Integer, Character> map = new TreeMap<>();
    map.put(1,'c');
    map.put(3,'b');
    map.put(2,'a');
    System.out.println(map.keySet()); // [1, 2, 3]
    System.out.println(map.values()); // 按照Key值顺序输出: [c, a, b]
    
    • comparator:自定义排序方式
    SortedMap<Integer, Character> map = new TreeMap<>(
        (key1, key2) -> -key1.compareTo(key2)
    );
    map.put(1, 'c');
    map.put(3, 'b');
    map.put(2, 'a');
    System.out.println(map.keySet()); // [3, 2, 1]
    
    • subMap:左闭右开 // [fromKey,toKey)
    SortedMap<Integer, Character> map = new TreeMap<>();
    map.put(1, 'c');
    map.put(3, 'b');
    map.put(2, 'a');
    map.put(4, 'e');
    SortedMap<Integer, Character> subMap = map.subMap(2, 4);
    System.out.println(subMap); // {2=a, 3=b}
    
    • firstKey和lastKey
    SortedMap<Integer, Character> map = new TreeMap<>();
    map.put(1, 'c');
    map.put(3, 'b');
    map.put(2, 'a');
    System.out.println("firstKey:"+map.firstKey()); // firstKey:1
    System.out.println("lastKey:"+map.lastKey()); // lastKey:3
    

    四 NavigableMap(继承SortedMap接口)

    1 NavigableMap接口中的方法

    方法 描述
    lowerKey(K):K 寻找小于传参的Key值,找不到则返回null
    lowerEntry(K):Entry<K,V>
    higherKey(K):K 寻找大于传参的Key值,找不到则返回null
    higherEntry(K):Entry<K,V>
    ceilingKey(K):K 寻找大于或等于传参的Key值,找不到则返回null
    ceilingEntry(K):Entry<K,V>
    floorKey(K):K 寻找小于或等于传参的Key值,找不到则返回null
    floorEntry(K):Entry<K,V>
    navigableKeySet():NavigableSet< K > 顺序keySet
    descendingKeySet():NavigableSet< K > 倒序keySet
    ...剩下的省略

    2 Demo

    NavigableMap<Integer, Character> map = new TreeMap<>();
    map.put(1, 'c');
    map.put(3, 'b');
    map.put(2, 'a');
    System.out.println(map.lowerKey(3)); // 2
    System.out.println(map.floorKey(3)); // 3
    System.out.println(map.higherKey(1)); // 2
    System.out.println(map.ceilingKey(1)); // 1
    System.out.println(map.navigableKeySet()); // [1, 2, 3]
    System.out.println(map.descendingKeySet()); // [3, 2, 1]
    

    五 AbstractMap

    public abstract class AbstractMap<K,V> implements Map<K,V> {
    	 public static class SimpleImmutableEntry<K,V> implements Entry<K,V>, java.io.Serializable{
            private static final long serialVersionUID = 7138329143949025153L;
    
            private final K key;
            private final V value;
             
            public V setValue(V value) {
                throw new UnsupportedOperationException();
            } 
            
         	// 对于不可变字段的set方法可以参考这个设计
            // ......
         }
        	
        public abstract Set<Entry<K,V>> entrySet();
        
        // keySet和values都使用了懒加载策略
        transient Set<K>        keySet;
        transient Collection<V> values;
        	
        public Set<K> keySet() {
            Set<K> ks = keySet;
            if (ks == null) {
                ks = new AbstractSet<K>() {
                    // ...... 基本实现方法
                };
                keySet = ks;
            }
            return ks;
        }
        
        public int size() {
            return entrySet().size();
        }
    	
        // 复用size方法:自己实现容器时也需要尽可能复用已有的方法,减少重复的代码。
        public boolean isEmpty() {
            return size() == 0;
        }
        
        // toString方法:对于容器的toString方法一般都需要遍历,遍历就涉及大量的字符串拼接,字符串拼接速度提升需要StringBuilder。
        public String toString() {
            Iterator<Entry<K,V>> i = entrySet().iterator();
            if (! i.hasNext())
                return "{}";
    
            StringBuilder sb = new StringBuilder();
            sb.append('{');
            for (;;) {
                Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                sb.append(key   == this ? "(this Map)" : key);
                sb.append('=');
                sb.append(value == this ? "(this Map)" : value);
                if (! i.hasNext())
                    return sb.append('}').toString();
                sb.append(',').append(' ');
            }
        }
        
        // get remove containsValue containsKey都依赖抽象方法:entrySet():Set
        // Set -> Collection -> Iterable 
        // AbstractMap的遍历都依赖Iterable的iterator方法返回的Iterator,都采用了迭代器遍历。
        public V get(Object key) {
            Iterator<Entry<K,V>> i = entrySet().iterator();
            if (key==null) {
                while (i.hasNext()) {
                    Entry<K,V> e = i.next();
                    if (e.getKey()==null)
                        return e.getValue();
                }
            } else {
                while (i.hasNext()) {
                    Entry<K,V> e = i.next();
                    if (key.equals(e.getKey()))
                        return e.getValue();
                }
            }
            return null;
        }
        
        // ...没有需要注意的地方,省略了
    }
    

    六 TreeMap

    1 概述

    • Java版本的红黑树实现映射类
    • 红黑树的前序遍历是根据值的大小排序的,即TreeMap的Key值具有有序性
    • 学习过红黑树的童鞋,看这个类的实现不难

    2 JDK源码

    • 字段和常量
    private final Comparator<? super K> comparator;
    // 红黑树根节点
    private transient Entry<K,V> root;
    // 长度
    private transient int size = 0;
    // fail-fast实现字段
    private transient int modCount = 0;
    // 提供一些Set的操作方法:包装了一下TreeMap中的方法
    private transient EntrySet entrySet;
    // TreeMap的Key值具有有序性
    private transient KeySet<K> navigableKeySet;
    // 倒序Map
    private transient NavigableMap<K,V> descendingMap;
    // 节点状态常量
    private static final boolean RED   = false;
    private static final boolean BLACK = true;
    
    • 内部Entry类
    static final class Entry<K,V> implements Map.Entry<K,V> {
            K key;
            V value;
            Entry<K,V> left;
            Entry<K,V> right;
            Entry<K,V> parent;
            boolean color = BLACK;
    		// 省略构造函数 getter和setter equals toString方法
    }
    
    • 常用API:V put(K,V)
    public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) { // 根节点为空创建新节点
            compare(key, key); // null值检查,TreeMap不允许key为空
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent; // 寻找插入新节点的父节点
        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);// 如果key值相等,直接覆盖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); // 如果key值相等,直接覆盖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;
    }
    
    private void fixAfterInsertion(Entry<K,V> x) {
        x.color = RED; // 新插入的节点为红色
        // 如果父亲节点和新子节点都为红色,表示破环了平衡
        // 看懂下面需要了解红黑树的保持平衡的操作:左旋转 右旋转 颜色反转
        while (x != null && x != root && x.parent.color == RED) { 
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }
    

    七 HashMap

    1 概述

    • 最常用的映射类,非线程安全
    • hash算法:计算hash值,数组取模,得到值。时间复杂度为常数,但由于hash冲突的存在并无法做到绝对的常数级别
    • hash冲突:
      • 链表:长度大于8时,转化为红黑树
      • 红黑树:查询效率高
    • 内部维护一个Node数组:可变长数组实现存在resize操作
    • 建议:请学习了hash算法红黑树数据结构再来看jdk的源码,不然效率太低。

    2Jdk源码

    • 字段和常量
    // 实际存放K和V的节点数组,Node实现Entry接口
    transient Node<K,V>[] table;
    
    // 采用懒加载机制,entrySet()返回值
    transient Set<Map.Entry<K,V>> entrySet;
    
    // 容器现在的大小
    transient int size;
    
    // fail-fast相关,请参考:https://www.cnblogs.com/linzhanfly/p/9571180.html
    transient int modCount;
    
    // 扩容时机:capacity * load factor
    int threshold;
    
    // 负载因子:决定容器装载到何种程度需要扩容(如:装了50%就希望扩容,则设置为0.5)
    final float loadFactor;
    
    // 默认负载因子
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    
    // 最大容量值
    static final int MAXIMUM_CAPACITY = 1 << 30;
    
    // 默认负载因子
    // 为什么选择0.75?请参考:https://www.jianshu.com/p/64f6de3ffcc1
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
    // hash冲突的存在,不同的key值可能同一个hash值。
    // hash冲突时,默认将新的key添加在链表末尾,但是随着链表的增大,查询效率越来越低(链表只能遍历查询,复杂度O(n))。
    // java8为了解决这个问题,通过将链表转换成红黑树来提高查询效率(复杂度O(lgn))
    // 那链表什么时候转换成红黑树?当链表长度大于TREEIFY_THRESHOLD时
    static final int TREEIFY_THRESHOLD = 8;
    
    // 那红黑树什么时候转换成链表?当链表长度小于TREEIFY_THRESHOLD时
    // 为什么TREEIFY_THRESHOLD和UNTREEIFY_THRESHOLD为什么不相等?
    // 因为震荡问题,假设加入某个值后,链表长度变成8,链表转换成红黑树;下一次操作又删除了该值,红黑树转换成链表。转换过程需要浪费一定的时间,具体的复杂度分析起来很复杂,以后有机会具体分析一下。
    // 于是设置比TREEIFY_THRESHOLD小一点可以缓解震荡导致的复杂度飙升问题。
    static final int UNTREEIFY_THRESHOLD = 6;
    
    // size到达threshold大小会进行扩容
    // table比较小时,很容易导致扩容;所以需要限制一下链表转红黑树的最小table大小
    // 如果不设置,连续的8次冲突就链表转红黑树,添加多4次,就resize了,hash也重算了,这样就不划算。
    static final int MIN_TREEIFY_CAPACITY = 64;
    
    • 构造方法
    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);
    }
    
    // 根据容量值计算table数组所需的大小,返回值一定为2的幂次方
    // 获取cap-1的二进制表达中最左边的1后面全变为1,如01000000 -> 01111111 ,最后+1 -> 10000000
    static final int tableSizeFor(int cap) {
        int n = cap - 1; // 01000000
        n |= n >>> 1; // 01000000 移1位-> 00100000 或运算-> 01100000
        n |= n >>> 2; // 01100000 移2位-> 00011000 或运算-> 01111000 
        n |= n >>> 4; // 01111000 移4位-> 00000111 或运算-> 01111111
        n |= n >>> 8; // ...省略
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
    
    // ...... 其他构造方法复用了该构造方法
    
    • Node节点
    // 链表节点
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next; 
    	
        // ......
        
        // hashCode和equals与key和value相关
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
    
        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;
        }
    }
    
    // 红黑树节点
    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
    
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
        }
    
        // 循环获取根节点
        final TreeNode<K,V> root() {
            for (TreeNode<K,V> r = this, p;;) {
                if ((p = r.parent) == null) return r;
                r = p;
            }
        }
    
        // 链表 -> 树
        final void treeify(Node<K,V>[] tab) {
            TreeNode<K,V> root = null;
            for (TreeNode<K,V> x = this, next; x != null; x = next) {
                next = (TreeNode<K,V>) x.next;
                x.left = x.right = null;
                if (root == null) {
                    x.parent = null;
                    x.red = false; // 根节点为黑
                    root = x;
                }else {
                    K k = x.key;
                    int h = x.hash;
                    Class<?> kc = null;
                    TreeNode<K,V> p = root;
                    for (;;) {
                        int dir, ph;
                        K pk = p.key;
                        if ((ph = p.hash) > h) dir = -1;
                        else if (ph < h) dir = 1;
                        else if ((kc == null &&
                                  (kc = comparableClassFor(k)) == null) ||
                                 (dir = compareComparables(kc, k, pk)) == 0)
                            dir = tieBreakOrder(k, pk);
    
                        TreeNode<K,V> xp = p;
                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
                            x.parent = xp;
                            if (dir <= 0) xp.left = x;
                            else xp.right = x;
                            root = balanceInsertion(root, x); //左旋或者右旋来保持红黑树的黑平衡
                            break;
                        }
                   }
                }
            }
            moveRootToFront(tab, root);
        }
    
        // 树 -> 链表
        final Node<K,V> untreeify(HashMap<K,V> map) {
            Node<K,V> hd = null, tl = null;
            for (Node<K,V> q = this; q != null; q = q.next) {
                Node<K,V> p = map.replacementNode(q, null);
                if (tl == null) hd = p;
                else tl.next = p;
                tl = p;
            }
            return hd;
        }
    
        // HashMap.replacementNode方法
        Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
            return new Node<>(p.hash, p.key, p.value, next);
        }
    }
    
    • get方法
    public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
    
    // hash值 =  高16位 低16位(高16位与低16位的异或)
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        // 第一个if:判断Map中有数据,并取出first = tab[(n - 1) & hash]
        if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { 
            // hash相等与key值相等则返回
            if (first.hash == hash && 
                ((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;
    }
    
    • put方法
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    
    // evict字段在HashMap无实现
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        // 如果tab数组为空或者长度为0,则扩容。
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        // 没有hash冲突则直接赋值
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            // 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;
                }
            }
            
            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;
    }
    
    • 剩余API自行研究......
  • 相关阅读:
    Solution: Win 10 和 Ubuntu 16.04 LTS双系统, Win 10 不能从grub启动
    在Ubuntu上如何往fcitx里添加输入法
    LaTeX 笔记---Q&A
    Hong Kong Regional Online Preliminary 2016 C. Classrooms
    Codeforces 711E ZS and The Birthday Paradox
    poj 2342 anniversary party
    poj 1088 滑雪
    poj 2479 maximum sum
    poj 2481 cows
    poj 2352 stars
  • 原文地址:https://www.cnblogs.com/linzhanfly/p/9957675.html
Copyright © 2011-2022 走看看