原谅我的无知,之前都没有听说过还有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());
}
}
}