java容器类:LinkedHashMap

LinkedHashMap基于HashMap实现,继承了HashMap.,存储数据的方式和HashMap一致(将键值对映射为entry对象).

  • 不同之处
    LinkedHashMap还在内部维护了一个链表.
    LinkedHashMap的内部数据结构类Entry继承了HashMap的Entry类,额外添加了before节点和after节点,还额外实现了remove,addBefore,recordAccess,recordRemoval方法用于构造链表

这个链表是怎么形成的?#

1.初始化链表头##

LinkedHashMap在实例化的时候会指定超类的构造器并进行实例化,超类实例化时会以多态的方式(动态绑定)来调用LinkedHashMap中重写的init方法,init方法被用来初始化链表的头结点.

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;
    threshold = initialCapacity;
    init()//HashMap中的init()方法是空的,但是子类继承该类后通过重写的方式实现init方法.HashMap类可以说一个基础实现,它有几个方法都是空的,是为子类实现做准备.
}

@Override
void init() {
    header = new Entry<>(-1, null, null, null);//构造一个空的entry
    header.before = header.after = header;//头节点和尾节点都指向自身.
}

2.向链表中添加数据##

  • LinkedHashMap中还有一个关键的布尔类型变量accessOrder,默认为false,值为false时,向LinkedHashMap插入数据其实是调用HashMap的put方法.
  1. 对于put方法如果,key已经在HashMap中,那么仅仅用新的值替换旧的值并返回旧的值.
  2. 反之就插入一个新的键值对,即在HashMap中构造并加入一个新的entry.
  3. 那么插入一个entry实质上是调用addEntry方法,该方法不仅在HashMap中有实现,在LinkedHashMap中亦有实现.LinkedHashMap的实现在父类的基础上更加入了将entry插入链表的操作.
  4. 所以LinkedHashMap插入数据时不仅向HashMap的table数组中插入一个entry,而且将该entry放入了LinkedHashMap自身实现的链表中.
  5. 值为true时,会在LinkedHashMap执行put,putAll,get操作时,改变链表结构,将最近访问的entry放在链表的尾部.
//HashMap的addEntry方法
void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }

    createEntry(hash, key, value, bucketIndex);
}


//HashMap上调用的相应的方法都会被重写
//LinkedHashMap的addEntry方法
void addEntry(int hash, K key, V value, int bucketIndex) {
    super.addEntry(hash, key, value, bucketIndex);//在HashMap的基础上更完善了自身的功能

    // Remove eldest entry if instructed
    Entry<K,V> eldest = header.after;
    if (removeEldestEntry(eldest)) {//removeEldestEntry可以由我们自己来重写,下面将会实现该效果
        removeEntryForKey(eldest.key);
    }
//LinkedHashMap的createEntry方法
void createEntry(int hash, K key, V value, int bucketIndex) {
    HashMap.Entry<K,V> old = table[bucketIndex];//对应位置的Entry被提取出来
    Entry<K,V> e = new Entry<>(hash, key, value, old);//加入新的Entry的末尾
    table[bucketIndex] = e;//新的Entry加入该位置
    e.addBefore(header);//调用LinkedHashMap内部Entry类的方法
    size++;
}

private void addBefore(Entry<K,V> existingEntry) {
        after  = existingEntry;        //header被设为新的Entry的尾节点
        before = existingEntry.before; //header的头结点被设为新的Entry的头结点,实际上header被初始化后,它的头结点和尾节点都为自身
        before.after = this;    //把新的Entry的节点同样地设为header的头结点和尾节点
        after.before = this;    //可以理解为两个类形成一个环形链.
    }
//

执行一次put操作,LinkHashMap中执行步骤




每个数据的插入以此类推

前面说到accessOrder值为true时,会在LinkedHashMap执行put,putAll,get操作时,改变链表结构,将最近访问的entry放在链表的尾部.
起作用的方法是Entry内实现的recordAccess方法,在执行get,put,putAll方法都能调用到该方法

void recordAccess(HashMap<K,V> m) {
        LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
        if (lm.accessOrder) {
            lm.modCount++;
            remove();    //断开与头结点和尾节点的连接
            addBefore(lm.header);//将Entry加到链表的尾部
        }
    }

 private void remove() {
        before.after = after;
        after.before = before;
    }

用代码举例

   LinkedHashMap<String, String> a = new LinkedHashMap<String, String>(16, 0.75f, true);//accessOrder为true
   LinkedHashMap<String, String> b = new LinkedHashMap<String, String>(16, 0.75f, false);//accessOrder为false
	//a和b都以相同顺序加入键值对,都调用get方法,然后查看通过Iterator输出的值
            a.put("1", "1");
	a.put("2", "2");
	a.put("3", "3");
	b.put("1", "1");
	b.put("2", "2");
	b.put("3", "3");
	a.get("1");
	b.get("1");
	Iterator<Entry<String, String>> i = a.entrySet().iterator();
	Iterator<Entry<String, String>> m = b.entrySet().iterator();
	
	while(i.hasNext()) {
		System.out.println("a输出的值:"+i.next().getValue());
	}
	while(m.hasNext()) {
		System.out.println("b输出的值:"+m.next().getValue());
	}

a输出的值:2
a输出的值:3
a输出的值:1
b输出的值:1
b输出的值:2
b输出的值:3
因为1被调用过,所以被放到了最后输出

在addEntry方法中还有一个可以让我们拓展的功能

 // Remove eldest entry if instructed
    Entry<K,V> eldest = header.after;
    if (removeEldestEntry(eldest)) {
        removeEntryForKey(eldest.key);
    }

//默认返回false,我们可以重写这个方法
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
    return false;
}

LinkedHashMap<String, String> c = new LinkedHashMap<String, String>(){
		private static final int MAX_ENTRIES = 2;
	     
          protected boolean removeEldestEntry(Map.Entry eldest) {
             return size() > MAX_ENTRIES;
          }
	};

 c.put("1", "1");
 c.put("2", "2");
 c.put("3", "3"); 

Iterator<Entry<String, String>> n = c.entrySet().iterator();
     while(n.hasNext()) {
			System.out.println("c输出的值:"+n.next().getValue());
		}
}

c输出的值:2
c输出的值:3

  • c中值允许存在MAX_ENTRIES数量的键值对
  • 再向其中添加会把最初添加的键值对删除.换句话说,map中只会存在最新的entry
原文地址:https://www.cnblogs.com/itsrobin/p/5140522.html