java源码解读之LinkedHashMap------jdk 1.7

来源:互联网 发布:云购cms漏洞 编辑:程序博客网 时间:2024/05/18 00:06

前面分析了HashMap的实现,我们知道其底层数据存储是一个hash表(数组+单向链表)。接下来我们看一下另一个LinkedHashMap,它是HashMap的一个子类,他在HashMap的基础上维持了一个双向链表(hash表+双向链表),在遍历的时候可以使用插入顺序(先进先出,类似于FIFO),或者是最近最少使用(LRU)的顺序。

 来具体看下LinkedHashMap的实现。1.定义  
public class LinkedHashMap<K,V>    extends HashMap<K,V>    implements Map<K,V>
2. 属性
//其他属性从hashmap处继承//头结点private transient Entry<K,V> header;//引用自网上:当accessOrder为true时表示LinkedHashMap内部节点按照访问次数排序,最老的节点也就是访问最少的节点.当 accessOrder为false时表示LinkedHashMap内部节点按照插入顺序排序,最老的节点也就是最早插入的节点,该值默认为false.private final boolean accessOrder;//其他基本一致,来看看entry是怎么重写的才能实现双向链表private static class Entry<K,V> extends HashMap.Entry<K,V> {    //属性有前后节点,实现双向链表的关键    Entry<K,V> before, after;    Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {        //直接调用父类的构造方法        super(hash, key, value, next);    }    private void remove() {        //例如有三个节点,第一个节点的下一个节点指向第三个节点,第三个节点的上一个节点指向第一个节点        before.after = after;        after.before = before;    }    //把一个节点加到当前节点的前    private void addBefore(Entry<K,V> existingEntry) {        //将当前节点的下一个节点指向传入节点        after = existingEntry;        //把当前节点的前一个节点指向传入节点的上一个节点        before = existingEntry.before;        //改变前后节点的指向为自己        before.after = this;        after.before = this;    }    //在hashmap调用put相同key的时候,get,putForNullKey的时候调用    void recordAccess(HashMap<K,V> m) {        LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;        //如果开启算法排序        if (lm.accessOrder) {            //修改次数+1            lm.modCount++;            //先删除再添加,进行排序            //删除当前节点            remove();            //把当前元素加入到header前            addBefore(lm.header);        }    }    //删除当前元素    void recordRemoval(HashMap<K,V> m) {        remove();    }}
3. 构造器
//自己设定临界值和负载因子public LinkedHashMap(int initialCapacity, float loadFactor) {    super(initialCapacity, loadFactor);    accessOrder = false;}//自己设置临界值public LinkedHashMap(int initialCapacity) {    super(initialCapacity);    accessOrder = false;}//使用默认属性public LinkedHashMap() {    super();    accessOrder = false;}//根据传入的map集合创建LinkedHashMappublic LinkedHashMap(Map<? extends K, ? extends V> m) {    super(m);    accessOrder = false;}//自己设定临界值和负载因子,是否开启算法排序public LinkedHashMap(int initialCapacity,                     float loadFactor,                     boolean accessOrder) {    super(initialCapacity, loadFactor);    this.accessOrder = accessOrder;}
4. 解析部分方法源码4.1 解析部分源码    LinkedHashMap是继承hashmap的,所以增删改查方法基本一致,只是重写了其中几个方法
void transfer(HashMap.Entry[] newTable, boolean rehash) {    int newCapacity = newTable.length;    //从hashmap的foreach遍历改为从头结点开始遍历    for (Entry<K,V> e = header.after; e != header; e = e.after) {        //找出应该放置的位置        if (rehash)            e.hash = (e.key == null) ? 0 : hash(e.key);        int index = indexFor(e.hash, newCapacity);        //把newTable[index]原来位置的结点改为当前节点e节点的下一个节点        e.next = newTable[index];        //把newTable[index]赋值为当前节点e        newTable[index] = e;    }}void init() {    //创建LinkedHashMap前,先创建头节点    header = new Entry<>(-1, null, null, null);    header.before = header.after = header;}public V get(Object key) {    //如果通过key查找不到对应entry,则直接返回null    Entry<K,V> e = (Entry<K,V>)getEntry(key);    if (e == null)        return null;    //与hashmap最大的不同,如果开启了算法排序,则对entry进行排序    e.recordAccess(this);    return e.value;}public void clear() {    super.clear();    //LinkedHashMap与hashmap不同的清空之处在于他有一个头节点的前后关系需要清理    header.before = header.after = header;}void addEntry(int hash, K key, V value, int bucketIndex) {    super.addEntry(hash, key, value, bucketIndex);    //判断是否需要删除头结点,因为header不存元素,所以是header的下一个元素,默认是false    Entry<K,V> eldest = header.after;    if (removeEldestEntry(eldest)) {        removeEntryForKey(eldest.key);    }}void createEntry(int hash, K key, V value, int bucketIndex) {    //正常的添加节点    HashMap.Entry<K,V> old = table[bucketIndex];    Entry<K,V> e = new Entry<>(hash, key, value, old);    table[bucketIndex] = e;    //维护节点的前后关系    e.addBefore(header);    size++;}
0 0
原创粉丝点击