手撕算法——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

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值