其次:关于afterNodeAccess()方法,在HashMap中没给具体实现,而在LinkedHashMap重写了,目的是保证操作过的Node节点永远在最后,从而保证读取的顺序性,在调用put方法和get方法时都会用到。
/** * 当accessOrder为true并且传入的节点不是最后一个时,将传入的node移动到最后一个 */ void afterNodeAccess(Node<K,V> e) { //在执行方法前的��一次的尾结点 LinkedHashMap.Entry<K,V> last; //当accessOrder为true并且传入的节点并不是上一次的尾结点时,执行下面的方法 if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; //p:当前节点 //b:当前节点的前一个节点 //a:当前节点的后一个节点; //将p.after设置为null,断开了与后一个节点的关系,但还未确定其位置 p.after = null; /** * 因为将当前节点p拿掉了,那么节点b和节点a之间断开了,我们先站在节点b的角度建立与节点a * 的关联,如果节点b为null,表示当前节点p是头结点,节点p拿掉后,p的下一个节点a就是头节点了; * 否则将节点b的后一个节点设置为节点a */ if (b == null) head = a; else b.after = a; /** * 因为将当前节点p拿掉了,那么节点a和节点b之间断开了,我们站在节点a的角度建立与节点b * 的关联,如果节点a为null,表示当前节点p为尾结点,节点p拿掉后,p的前一个节点b为尾结点, * 但是此时我们并没有直接将节点p赋值给tail,而是给了一个局部变量last(即当前的最后一个节点),因为 * 直接赋值给tail与该方法最终的目标并不一致;如果节点a不为null将节点a的前一个节点设置为节点b * * (因为前面已经判断了(last = tail) != e,说明传入的节点并不是尾结点,既然不是尾结点,那么 * e.after必然不为null,那为什么这里又判断了a == null的情况? * 以我的理解,Java可通过反射机制破坏封装,因此如果都是反射创建出的Entry实体,可能不会满足前面 * 的判断条件) */ if (a != null) a.before = b; else last = b; /** * 正常情况下last应该也不为空,为什么要判断,原因和前面一样 * 前面设置了p.after为null,此处再将其before值设置为上一次的尾结点last,同时将上一次的尾结点 * last设置为本次p */ if (last == null) head = p; else { p.before = last; last.after = p; } //最后节点p设置为尾结点,完事 tail = p; ++modCount; } }我们前面说到的linkNodeLast(Entry e)方法和现在的afterNodeAccess(Node e)都是将传入的Node节点放到最后,那么它们的使用场景如何呢?
在前面讲解HashMap时,提到了HashMap的put流程,如果在对应的hash位置上还没有元素,那么直接new Node()放到数组table中,这个时候对应到LinkedHashMap中,调用了newNode()方法,就会用到linkNodeLast(),将新node放到最后,而如果对应的hash位置上有元素,进行元素值的覆盖时,就会调用afterNodeAccess(),将原本可能不是最后的node节点拿到了最后。如
LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, true); map.put("1月", 20); //此时就会调用到linkNodeLast()方法,也会调用afterNodeAccess()方法,但会被阻挡在 //if (accessOrder && (last = tail) != e) 之外 map.put("2月", 30); map.put("3月", 65); map.put("4月", 43); //这时不会调用linkNodeLast(),会调用afterNodeAccess()方法将key为“1月”的元素放到最后 map.put("1月", 35); //这时不会调用linkNodeLast(),会调用afterNodeAccess()方法将key为“2月”的元素放到最后 map.get("2月"); //调用打印方法 for (Map.Entry<String, Integer> entry : map.entrySet()){ System.out.println("key: " + entry.getKey() + ", value: " + entry.getValue()); }结果如下:
key: 3月, value: 65 key: 4月, value: 43 key: 1月, value: 35 key: 2月, value: 30而如果是执行下面这段代码,将accessOrder改为false
LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, false); map.put("1月", 20); //此时就会调用到linkNodeLast()方法,也会调用afterNodeAccess()方法,但会被阻挡在 //if (accessOrder && (last = tail) != e) 之外 map.put("2月", 30); map.put("3月", 65); map.put("4月", 43); //这时不会调用linkNodeLast(),会调用afterNodeAccess()方法将key为“1月”的元素放到最后 map.put("1月", 35); map.get("2月"); //调用打印方法 for (Map.Entry<String, Integer> entry : map.entrySet()){ System.out.println("key: " + entry.getKey() + ", value: " + entry.getValue()); }结果如下:
key: 1月, value: 35 key: 2月, value: 30 key: 3月, value: 65 key: 4月, value: 43