LinkedHashMap使用技巧

public class LinkedHashMap   extends HashMap  implements Map

的两个使用技巧

1.元素先进先出。

 public LinkedHashMap(int initialCapacity) 

这是linkedHashMap的一个构造方法,从名字上来看,是初始化长度。该构造直接调用supper(initialCapacity),HashMap里对这个参数的解释是:它参与了一些与负载因子有关运算。

 this.threshold = tableSizeFor(initialCapacity);//457行

所以尽量设置为预估长度。

如果没有其他的操作,我们在使用过程中,这个长度的作用是看不出来的。如下代码。

public static void main(String[] args) {
		LinkedHashMap map = new LinkedHashMap(5);
		map.put(1, 1);
		map.put(2, 2);
		map.put(3, 3);
		map.put(4, 4);
		map.put(5, 5);
		map.put(6, 6);
		System.out.println(map);//{1=1, 2=2, 3=3, 4=4, 5=5, 6=6}
		
	}

尽管我们设定为5,但是现在map的长度还是6个,并不因为我们的设定而不让Put。

但是我们可以让linkedHashMap容量就是为5且先进先出。让上面代码里的打印变成

{2=2, 3=3, 4=4, 5=5, 6=6}。

看如下代码:

public class MyMap extends LinkedHashMap{
	private int capacity;
	public MyMap(int initialCapacity) {
		super(initialCapacity);
		this.capacity = initialCapacity;
	}
	@Override
	protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
		return size() > capacity;
	}
}

我们新建了一个类,继承了linkedHashMap,重写了一个方法而已。

然后测试一下这个map

public static void main(String[] args) {
		MyMap map = new MyMap(5);
		map.put(1, 1);
		map.put(2, 2);
		map.put(3, 3);
		map.put(4, 4);
		map.put(5, 5);
		map.put(6, 6);
		System.out.println(map);//{2=2, 3=3, 4=4, 5=5, 6=6}
		
	}

ok,打印了{2=2, 3=3, 4=4, 5=5, 6=6},变成了先进先出的FIFO结构。

原理解析:

原因在put方法里面。追查put,最终来到了HashMap的putVal的方法里,看下该方法的最后几行源码:

         ......  

        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;

关键在afterNodeInsertion(evict)里面。evict就是设定好的true。

接着进入到afterNodeInsertion方法里,发现上面都没有,这分明就是put结束之后让子类重写一些特殊操作的节奏啊。再查看LinkedHashMap的afterNodeInsertion:

 void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMap.Entry first;
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }

这里的意思是如果 true 且 第一个参数不是null 且  removeEldestEntry返回true ,那么就把第一个删除了。

我们再看下LinkedHashMap的removeEldestEntry方法,发现只返回了false,也就是说LinkedHashMap永远不会触发这个方法。

但是我们自己写的MyMap又重写了removeEldestEntry这个方法,返回判断 Map.size() 是否大于设定长度 capacity。

那么使用MyMap如果超出了长度就会返回true,则LinkedHashMap的 afterNodeInsertion方法就会删除掉第一个,最终形成了固定长度,先进先出的功能。

2.最少使用的元素和最常使用的元素

需求是设计一个Map,能够知道哪些元素是最近使用(存取)的,哪些元素是最少使用的。可能有些同学就在元素里面添加了时间戳了。但是LinkedHashMap提供了一种高效的方式来实现了这个功能,我们使用这个构造。

 public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) 

将accessOrder设置为true。

public static void main(String[] args) {
		LinkedHashMap map = new LinkedHashMap(1000, 0.75f, true);
		map.put(1, 1);
		map.put(2, 2);
		map.put(3, 3);
		map.put(4, 4);
		map.put(5, 5);
		map.put(6, 6);
		System.out.println(map);//{1=1, 2=2, 3=3, 4=4, 5=5, 6=6}
		map.put(5, 55);
		System.out.println(map);//{1=1, 2=2, 3=3, 4=4, 6=6, 5=55}
		map.get(2);
		System.out.println(map);//{1=1, 3=3, 4=4, 6=6, 5=55, 2=2}
		
	}

可以看到无论是存还是取,都会把当前元素从当前位置删除,放到最后。

那么这个Map放在最后的永远是最近使用的,最前面的则是最少使用的。

原理:

LinkedHashMap重写了afterNodeAccess这个方法。afterNodeAccess的作用就是把当前节点删除,然后放到最后面。

而如果构造里的accessOrder如果为true,则会出现以下情况:

在get时会触发afterNodeAccess方法,在put(Key,Val)时,如果已经存在key,也会触发afterNodeAccess。

 

 

 

你可能感兴趣的:(#,JAVA源码阅读,#,JAVA容器)