聊聊LinkedHashMap

本文深入解析了LinkedHashMap的数据结构,揭示其如何结合哈希表和双向链表实现有序且高效的键值对存储。适用于需要保持元素插入顺序或最近最少使用(LRU)缓存策略的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  LinkedHashMap是一个根据某种规则有序的hashmap。根据名字,我们也可以看出这个集合是有hash散列的功能的同时也有顺序。hashmap是无法根据某种顺序来访问数据的,例如放入集合的元素先后的顺序。list都有这个功能,可以根据放入集合的先后来访问具体的数据。这里大家也肯定是有疑问的,例如都已经使用了hash了,为什么还要去保证顺序访问。这个在后面的场景中解释。

  LinkedHashMap的实现

  当刚遇到这个集合的时候,我也疑惑,能同时满足条件的数据结构究竟是怎么样的。如果没有思考这个问题,还请看到这里好好想想。

  ---------滑稽分割--------

  答案确实是没有这样的数据结构。他是两种结构的组合。一种是我们熟悉的hashmap。另外一种就是链表。数据存入集合的时候,先根据hashmap的流程存放入数组中。然后再根据链表的原则,进行链接。

  如果看源码,也会发现其实没有多少方法。基本都是继承自hashmap的。

  public class LinkedHashMapK,V

  extends HashMapK,V

  implements MapK,V

  我们来看看串联逻辑的几个操作

  节点组成

  static class EntryK,V extends HashMap.NodeK,V {

  EntryK,V before, after;

  Entry(int hash, K key, V value, NodeK,V next) {

  super(hash, key, value, next);

  }

  }

  可以看出。相比hashmap的节点。linkedhashmap主要增加before, after。可以组成一个双向链表。

  节点加入

  private void linkNodeLast(LinkedHashMap.EntryK,V p) {

  LinkedHashMap.EntryK,V last = tail;//获取最后一个节点

  tail = p;//tail指向新加入的节点

  if (last == null)//链表为空

  head = p;

  else {

  p.before = last;

  last.after = p;

  }

  }

  //新建节点的时候把节点加入了双向链表

  NodeK,V newNode(int hash, K key, V value, NodeK,V e) {

  LinkedHashMap.EntryK,V p =

  new LinkedHashMap.EntryK,V(hash, key, value, e);

  linkNodeLast(p);

  return p;

  }

  删除情况也是类似。

  节点访问

  public V get(Object key) {

  NodeK,V e;

  if ((e = getNode(hash(key), key)) == null)

  return null;

  if (accessOrder)//是否根据某种顺序

  afterNodeAccess(e);//把访问节点加入到尾节点

  return e.value;

  }

  afterNodeAccess中主要是把访问的节点从原来的位置摘除,加入到尾节点,成为链表的最后一个元素。

  使用场景

  顺序遍历和快速定位

  LinkedHashMap适合有加入顺序和快速定位的场景。我自己开发中遇到过一个场景,就是把配置顺序读取,需要按照读取的顺序访问,而且还需要根据值key直接获取值。这个场景就需要使用LinkedHashMap。

  缓存

  LinkedHashMap另外一个强大的功能就是做缓存。不过我们要继承一下。去重写一个方法。

  protected boolean removeEldestEntry(Map.EntryK,V eldest) {//默认是没有操作

  return false;

  }

  这个方法在插入之后会被调用到。

  void afterNodeInsertion(boolean evict) { // possibly remove eldest

  LinkedHashMap.EntryK,V first;

  if (evict (first = head) != null removeEldestEntry(first)) {//删除第一个元素

  K key = first.key;

  removeNode(hash(key), key, null, false, true);

  }

  }

  LinkedHashMap支持两种缓存策略。FIFO和LRU。大家应该也猜到控制策略的地方就是accessOrder。默认为false。就是FIFO。设置为true时就是LRU。因为在访问的时候会调整链表结构,调用afterNodeAccess会把访问的节点放入队列最后。所以每次删除first就可以达到效果。我一般会选择继承LinkedHashMap。然后重写removeEldestEntry,例如可以元素个数达到200范围true。这里需要根据具体场景来编写。

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值