手撕算法——LinkedHashMap相關的題型

原諒我的無知,之前都沒有聽說過還有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());
		  }
		
	}

}

參考鏈接
Java集合系列之LinkedHashMap

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章