LinkedHashMap和LruCacha

1.LinkedHashMap简介:

1.HashMap元素之间是无序的,LinkedHashMap可以有序地存储元素;
2.LinkedHashMap中的顺序分为插入顺序访问顺序,可以通过accessOrder来修改这一属性;
3.LinkedHashMap有序的特性是用双向链表来实现的;
4.LinkedHashMap不是线程安全的
在这里插入图片描述

2.测试:

1.HashMap无序性:

    HashMap<String, Integer> map = new HashMap<>();
    map.put("key1", 1);
    map.put("key2", 2);
    map.put("key3", 3);
    Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry entry = iterator.next();
        Log.e("TTTTTag", entry.getKey() + "     " + entry.getValue());
    }

打印结果:

TTTTTag: key2       2
TTTTTag: key1       1
TTTTTag: key3       3

2.LinkedHashMap有序性:

    LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, false);
    map.put("key1", 1);
    map.put("key2", 2);
    map.put("key3", 3);
    Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry entry = iterator.next();
        Log.e("TTTTTag", entry.getKey() + "     " + entry.getValue());
    }

打印结果:

TTTTTag: key1       1
TTTTTag: key2       2
TTTTTag: key3       3

3.get和put方法对LinkedHashMap顺序的影响:

	//注意这里最后一个参数为true的意思是,按照访问顺序排序;
	//如果最后一个参数为false,则get和put方法不会对链表顺序产生影响;
	LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, true);
    map.put("key1", 1);
    map.put("key2", 2);
    map.put("key3", 3);
    map.put("key4", 4);
    map.put("key5", 5);
            
    //map.put("key3", 3);    
    map.get("key3");	
            
    Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry entry = iterator.next();
        Log.e("TTTTTag", entry.getKey() + "       " + entry.getValue());
    }

打印结果:

TTTTTag: key1       1
TTTTTag: key2       2
TTTTTag: key4       4
TTTTTag: key5       5
TTTTTag: key3       3

可以看到被getput访问到的元素都被移动到了链表末尾

3.LinkedHashMap源码:
LinkedHashMap源码中的指针:

next用于指向在同一个桶中hash值相同的下一个元素
before和after用于指向维护顺序所用的双向链表的上一个和下一个元素

构造方法:
  public LinkedHashMap() {
        // 调用HashMap的构造方法,其实就是初始化Entry[] table
        super();
        // 按访问顺序,默认为false,所以是基于插入排序的
        accessOrder = false;
  }

构造方法中调用了初始化方法:

  void init() {
       // 创建了一个hash=-1,key、value、next都为null的Entry
       header = new Entry<>(-1, null, null, null);
       // 这个Entry继承了HashMap.Entry, 它增加了两个指针,before和after;
       // 让创建的Entry的before和after都指向自身;
       // 其实就是创建了一个只有头部节点的双向链表
       header.before = header.after = header;
  }

至此,我们的到了一个空的EntryTable,和一个未放入EntryTable的head头节点;在这里插入图片描述
LinkedHashMap类:

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{
	transient LinkedHashMapEntry<K,V> head;				//最老的节点
	transient LinkedHashMapEntry<K,V> tail;				//最新的节点
	final boolean accessOrder;
}
put方法:

1.put方法沿用了HashMap方法主体;
2.除了将元素放入hash表,还需要将这个节点加入以head为头节点的双向链表,这样就可以记录插入节点的顺序了
3.如果accessOrder为true,且要put的元素已经存在,需要将这个元素移动到链表末尾

get方法:

get方法也是沿用HashMap方法主体,如果accessOrder为true,需要将访问的元素移动到链表末尾

4.LruCacha
public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;
    
    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        // 注意第三个参数,将accessOrder设为了true,说明LruCache记录了访问顺序
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}

put方法:

public final V put(K key, V value) {
       if (key == null || value == null) {
           throw new NullPointerException("key == null || value == null");
       }

       V previous; 
       synchronized (this) {
           putCount++;
           //计算entry的大小
           size += safeSizeOf(key, value); 
           //将已经存在的以key为键的节点置换出来
           previous = map.put(key, value);
           if (previous != null) {
               //如果之前存在,这先减去之前那个entry所占用的内存大小
               size -= safeSizeOf(key, previous);
           }
       }

       if (previous != null) {
       //如果之前存在则调用entryRemoved回调子类重写的此方法,做一些处理
           entryRemoved(false, key, previous, value);
       }
       //根据最大的容量,计算是否需要淘汰掉最不常使用的entry
       trimToSize(maxSize);
       return previous;
   }

淘汰最不常用的节点:

    public void trimToSize(int maxSize) {
        while(true) {
            Object key;
            Object value;
            synchronized(this) {
                if (this.size < 0 || this.map.isEmpty() && this.size != 0) {
                    throw new IllegalStateException(xxx);
                }
				//如果节点个数已经小于最大容量,退出循环;
                if (this.size <= maxSize || this.map.isEmpty()) {
                    return;
                }
 				//当超出容量的时候,循环获取链表第一个元素(最不常用节点)并删除;
                Entry<K, V> toEvict = (Entry)this.map.entrySet().iterator().next();
                key = toEvict.getKey();
                value = toEvict.getValue();
                this.map.remove(key);
                this.size -= this.safeSizeOf(key, value);
                ++this.evictionCount;
            }

            this.entryRemoved(true, key, value, (Object)null);
        }
    }

get方法:

  public final V get(K key) {
      if (key == null) {
          throw new NullPointerException("key == null");
      }

      V mapValue;
      //根据key来查询符合条件的etnry
      synchronized (this) {
          mapValue = map.get(key);
          if (mapValue != null) {
              hitCount++;
              return mapValue;
          }
          missCount++;
      }
	  
	  //根据查到的东西新建一个节点
      V createdValue = create(key);
      if (createdValue == null) {
          return null;
      }

      synchronized (this) {
          createCount++;
          //把这个新建的节点插入LinkedHashMap,由LinkedHashMap源码可知新插入的节点位于链表尾部;
          //这样链表中就可能存在两个相同的节点了
          mapValue = map.put(key, createdValue);

          if (mapValue != null) {
              map.put(key, mapValue);
          } else {
              size += safeSizeOf(key, createdValue);
          }
      }
      
      if (mapValue != null) {
      	  //为了保证链表中不出现相同的元素,从链表中删除位置靠前的那个节点
          entryRemoved(false, key, createdValue, mapValue);
          return mapValue;
      } else {
          trimToSize(maxSize);
          return createdValue;
      }
 }
总结:

LinkedHashMap就是在HashMap的基础上把所有元素按顺序串成一个双向链表;
accessOrder = true时会记录访问顺序,访问包括get和put,这些操作会把访问的元素放在链表末端
accessOrder = false时只会按照元素添加顺序排序

LruCacha可以用LinkedHashMap简单实现,它只是一个记录访问顺序,且容量超限会从链表头删除元素的LinkedHashMap

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值