原諒我的無知,之前都沒有聽說過還有LinkedHashMap這個類,最近刷題的時候卻頻頻碰到與它相關的題型,如實現LRU緩存,實質上是考察對LinkedHashMap底層的理解。再如員工工資排序,使用LinkedHashMap可以輕鬆解決。所以讓我們來了解一下這個強大的java集合。
HashMap基礎知識
LinkedHashMap是什麼? 能幹啥
可以先看看jdk的中文手冊中的解釋:
LikedHashMap是哈希表和鏈表實現的Map接口,具有可預測的迭代次序。 這種實現不同於HashMap,它維持於所有條目的運行雙向鏈表。
也就是說LinkedHashMap有以下特點
(1)使用鏈表存儲,保證了它的有序性,這也是區別HashMap的地方
(2)它具有HashMap的特性,能以O(1)的複雜度查找到某個元素。
(3)LinkedHashMap還可以根據訪問的順序進行排序(注意是在插入順序基礎上進行排序)。
代碼測試
代碼來自於參考鏈接1
測試有序性
public static void main(String[] args) {
Map<String, String> map = new LinkedHashMap<String, String>();
//Map<String, String> map = new HashMap<String, String>(); 無序的
map.put("apple", "蘋果");
map.put("watermelon", "西瓜");
map.put("banana", "香蕉");
map.put("peach", "桃子");
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
使用HashMap測試結果
banana=香蕉
apple=蘋果
peach=桃子
watermelon=西瓜
使用LinkedHashMap測試結果
apple=蘋果
watermelon=西瓜
banana=香蕉
peach=桃子
測試修改後順序的改變
public static void main(String[] args) {
Map<String, String> map = new LinkedHashMap<String, String>(16,0.75f,true);
map.put("apple", "蘋果");
map.put("watermelon", "西瓜");
map.put("banana", "香蕉");
map.put("peach", "桃子");
map.get("banana");
map.get("apple");
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
代碼運行結果如下,可以看出訪問了banana和apple之後,map中的順序發生了改變
watermelon=西瓜
peach=桃子
banana=香蕉
apple=蘋果
底層實現
LinkedHashMap底層實現
LinkedHashMap有哪些功能呢?
實現了Map接口
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
數據存儲方式
底層使用Entry來保存節點的數據,before和after表明使用的是雙向鏈表
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
//雙向鏈表的鏈表頭
transient LinkedHashMap.Entry<K,V> head;
//雙向鏈表的鏈表尾部,記錄最新被修改的元素
transient LinkedHashMap.Entry<K,V> tail;
//是否排序
final boolean accessOrder;
get方法邏輯
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
相關問題
LRU緩存的實現(不使用LinkedHashMap)
class Node{
public int key;
public int val;
Node(int key,int val){
this.key=key;
this.val=val;
}
}
class LRUCache {
/*
緩存滿了的時候,選擇未使用的時間最長的置換出去
*/
private Deque<Node> deque;
private Map<Integer,Node> map;
private int cap;
private int size=0;
public LRUCache(int capacity) {
deque=new LinkedList<Node>();
map=new HashMap<Integer,Node>();
this.cap=capacity;
}
public int get(int key) {
if(!map.containsKey(key)){//不存在 則返回-1
return -1;
}
//存在 將其放到隊列的頭部,返回該值
Node tmp=map.get(key);
deque.remove(tmp);
deque.addFirst(tmp);
return tmp.val;
}
public void put(int key, int value) {
Node node=new Node(key,value);
//要插入的值已經在隊列中
if(map.containsKey(key)){
Node tmp=map.get(key);
//放到頭部
deque.remove(tmp);
deque.addFirst(node);
//更新map的值
map.put(key,node);
}else{
//判斷是否滿了
if(size==this.cap){
//刪除尾部
Node tmp= deque.removeLast();
map.remove(tmp.key);
size--;
}
deque.addFirst(node);
map.put(key,node);
size++;
}
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
使用HashMapLinkedList實現LRU緩存
class LRUCache extends LinkedHashMap<Integer, Integer>{
private int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75F, true);
this.capacity = capacity;
}
public int get(int key) {
return super.getOrDefault(key, -1);
}
public void put(int key, int value) {
super.put(key, value);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}
按出現頻次對工資進行排序
/*
問題描述
給N名員工的工資清單,按照工資分佈的人數降序排序,如果有兩個工資的人的數目一致(大概意思就是假設3000工資有3個人,5000也是三個人),就按照工資在給定清單的順序裏面排序。
輸入
4,1,4,4,2,1,2,3,1,9,1,2,8,7,5,6,3,4,3,6
輸出
4,4,4,4,1,1,1,1,2,2,2,3,3,3,6,6,9,8,7,5
思路
用Map來統計工資及其出現的頻次,由於需要對頻次相同的工資按照原來的順序打印,所以需要使用到是LinkedHashMap,Key爲工資,Value爲出現頻次;
用Map的EntrySet構造List, 並對list按照工資出現頻次排序;
將上述List從頭到尾添加至結果集List中,頻次爲n,則重複添加n次。
* */
public class Test3_01 {
public static void main(String[] args) {
LinkedHashMap<Integer, Integer> linkedHashMap=new LinkedHashMap<>();
int input[]= {4,1,4,4,2,1,2,3,1,9,1,2,8,7,5,6,3,4,3,6};
for(int i=0;i<input.length;i++) {
if(!linkedHashMap.containsKey(input[i])) {//不包含的話
linkedHashMap.put(input[i], 1);
}else {
linkedHashMap.put(input[i], linkedHashMap.get(input[i])+1);
}
}
Iterator<Entry<Integer, Integer> > iterator=linkedHashMap.entrySet().iterator();
while(iterator.hasNext()) {
Entry<Integer, Integer> node=iterator.next();
System.out.println(node.getKey()+" "+node.getValue());
}
System.out.println("=========================================");
List<Map.Entry<Integer,Integer>> list = new ArrayList<Map.Entry<Integer,Integer>>(linkedHashMap.entrySet());
Collections.sort(list,new Comparator<Map.Entry<Integer,Integer> >() {
@Override
public int compare(Entry<Integer, Integer> o1, Entry<Integer, Integer> o2) {
// TODO Auto-generated method stub
return o2.getValue()-o1.getValue();
}
});
for(Map.Entry<Integer, Integer> entry:list) {
System.out.println(entry.getKey()+" "+entry.getValue());
}
}
}