ImageVerifierCode 换一换
格式:DOCX , 页数:19 ,大小:22.74KB ,
资源ID:10624943      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/10624943.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Java和Android的LRU缓存及实现原理.docx)为本站会员(b****8)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Java和Android的LRU缓存及实现原理.docx

1、Java和Android的LRU缓存及实现原理一、概述Android提供了LRUCache类,可以方便的使用它来实现LRU算法的缓存。Java提供了LinkedHashMap,可以用该类很方便的实现LRU算法,Java的LRULinkedHashMap就是直接继承了LinkedHashMap,进行了极少的改动后就可以实现LRU算法。二、Java的LRU算法Java的LRU算法的基础是LinkedHashMap,LinkedHashMap继承了HashMap,并且在HashMap的基础上进行了一定的改动,以实现LRU算法。1、HashMap首先需要说明的是,HashMap将每一个节点信息存储在E

2、ntry结构中。Entry中存储了节点对应的key、value、hash信息,同时存储了当前节点的下一个节点的引用。因此Entry是一个单向链表。HashMap的存储结构是一个数组加单向链表的形式。每一个key对应的hashCode,在HashMap的数组中都可以找到一个位置;而如果多个key对应了相同的hashCode,那么他们在数组中对应在相同的位置上,这时,HashMap将把对应的信息放到Entry中,并使用链表连接这些Entry。 static class Entry implements Map.Entry final K key; V value; Entry next; int

3、hash; /* * Creates new entry. */ Entry(int h, K k, V v, Entry n) value = v; next = n; key = k; hash = h; public final K getKey() return key; public final V getValue() return value; public final V setValue(V newValue) V oldValue = value; value = newValue; return oldValue; public final boolean equals(

4、Object o) if (!(o instanceof Map.Entry) return false; Map.Entry e = (Map.Entry)o; Object k1 = getKey(); Object k2 = e.getKey(); if (k1 = k2 | (k1 != null & k1.equals(k2) Object v1 = getValue(); Object v2 = e.getValue(); if (v1 = v2 | (v1 != null & v1.equals(v2) return true; return false; public fina

5、l int hashCode() return Objects.hashCode(getKey() Objects.hashCode(getValue(); public final String toString() return getKey() + = + getValue(); /* * This method is invoked whenever the value in an entry is * overwritten by an invocation of put(k,v) for a key k thats already * in the HashMap. */ void

6、 recordAccess(HashMap m) /* * This method is invoked whenever the entry is * removed from the table. */ void recordRemoval(HashMap m) 下面贴一下HashMap的put方法的代码,并进行分析public V put(K key, V value) if (table = EMPTY_TABLE) inflateTable(threshold); if (key = null) return putForNullKey(value);/以上信息不关心,下面是正常的插

7、入逻辑。/首先计算hashCode int hash = hash(key);/通过计算得到的hashCode,计算出hashCode在数组中的位置 int i = indexFor(hash, table.length);/for循环,找到在HashMap中是否存在一个节点,对应的key与传入的key完全一致。如果存在,说明用户想要替换该key对应的value值,因此直接替换value即可返回。 for (Entry e = tablei; e != null; e = e.next) Object k; if (e.hash = hash & (k = e.key) = key | key

8、.equals(k) V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; /逻辑执行到此处,说明HashMap中不存在完全一致的kye.调用addEntry,新建一个节点保存key、value信息,并增加到HashMap中 modCount+; addEntry(hash, key, value, i); return null; 在上面的代码中增加了一些注释,可以对整体有一个了解。下面具体对一些值得分析的点进行说明。int i = indexFor(hash, table.length);

9、可以看一下源码:static int indexFor(int h, int length) / assert Integer.bitCount(length) = 1 : length must be a non-zero power of 2; return h & (length-1); 为什么获得的hashCode(h)要和(length-1)进行按位与运算?这是为了保证去除掉h的高位信息。如果数组大小为8(1000),而计算出的h的值为10(1010),如果直接获取数组的index为10的数据,肯定会抛出数组超出界限异常。所以使用按位与(0111&1010),成功清除掉高位信息,得到

10、2(0010),表示对应数组中index为2的数据。效果与取余相同,但是位运算的效率明显更高。但是这样有一个问题,如果length为9,获取得length-1信息为8(1000),这样进行位运算,不但不能清除高位数据,得到的结果肯定不对。所以数组的大小一定有什么特别的地方。通过查看源码,可以发现,HashMap无时无刻不在保证对应的数组个数为2的n次方。首先在put的时候,调用inflateTable方法。重点在于roundUpToPowerOf2方法,虽然它的内容包含大量的位相关的运算和处理,没有看的很明白,但是注释已经明确了,会保证数组的个数为2的n次方。private void infl

11、ateTable(int toSize) / Find a power of 2 = toSizeint capacity = roundUpToPowerOf2(toSize);threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);table = new Entrycapacity;initHashSeedAsNeeded(capacity);其次,在addEntry等其他位置,也会使用(2 * table.length)、table.length 1等方式,保证数组的个数为2的n次方。for (Ent

12、ry e = tablei; e != null; e = e.next)因为HashMap使用的是数组加链表的形式,所以通过hashCode获取到在数组中的位置后,得到的不是一个Entry,而是一个Entry的链表,一定要循环链表,获取key对应的value。addEntry(hash, key, value, i);先判断数组个数是否超出阈值,如果超过,需要增加数组个数。然后会新建一个Entry,并加到数组中。 /* * Adds a new entry with the specified key, value and hash code to * the specified bucke

13、t. It is the responsibility of this * method to resize the table if appropriate. * * Subclass overrides this to alter the behavior of put method. */ void addEntry(int hash, K key, V value, int bucketIndex) if (size = threshold) & (null != tablebucketIndex) resize(2 * table.length); hash = (null != k

14、ey) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); createEntry(hash, key, value, bucketIndex); /* * Like addEntry except that this version is used when creating entries * as part of Map construction or pseudo-construction (cloning, * deserialization). This version neednt worry about re

15、sizing the table. * * Subclass overrides this to alter the behavior of HashMap(Map), * clone, and readObject. */ void createEntry(int hash, K key, V value, int bucketIndex) Entry e = tablebucketIndex; tablebucketIndex = new Entry(hash, key, value, e); size+; 2、LinkedHashMapLinkedHashMap在HashMap的基础上,

16、进行了修改。首先将Entry由单向链表改成双向链表。增加了before和after两个队Entry的引用。 private static class Entry extends HashMap.Entry / These fields comprise the doubly linked list used for iteration. Entry before, after; Entry(int hash, K key, V value, HashMap.Entry next) super(hash, key, value, next); /* * Removes this entry fr

17、om the linked list. */ private void remove() before.after = after; after.before = before; /* * Inserts this entry before the specified existing entry in the list. */ private void addBefore(Entry existingEntry) after = existingEntry; before = existingEntry.before; before.after = this; after.before =

18、this; /* * This method is invoked by the superclass whenever the value * of a pre-existing entry is read by Map.get or modified by Map.set. * If the enclosing Map is access-ordered, it moves the entry * to the end of the list; otherwise, it does nothing. */ void recordAccess(HashMap m) LinkedHashMap

19、 lm = (LinkedHashMap)m; if (lm.accessOrder) lm.modCount+; remove(); addBefore(lm.header); void recordRemoval(HashMap m) remove(); 同时,LinkedHashMap提供了一个对Entry的引用header(private transient Entry header)。header的作用就是永远只是HashMap中所有成员的头(header.after)和尾(header.before)。这样把HashMap本身的数组加链表的格式进行了修改。在LinkedHashMa

20、p中,即保留了HashMap的数组加链表的数据保存格式,同时增加了一套header作为开始标记的双向链表(我们暂且称之为header的双向链表)。LinkedHashMap就是通过header的双向链表来实现LRU算法的。header.after永远指向最近最不常使用的那个节点,删除的话,就是删除这个header.after对应的节点。相对的,header.before指向的就是刚刚使用过的那个节点。LinkedHashMap并没有提供put方法,但是LinkedHashMap重写了addEntry和createEntry方法,如下: /* * This override alters beh

21、avior of superclass put method. It causes newly * allocated entry to get inserted at the end of the linked list and * removes the eldest entry if appropriate. */ void addEntry(int hash, K key, V value, int bucketIndex) super.addEntry(hash, key, value, bucketIndex); / Remove eldest entry if instructe

22、d Entry eldest = header.after; if (removeEldestEntry(eldest) removeEntryForKey(eldest.key); /* * This override differs from addEntry in that it doesnt resize the * table or remove the eldest entry. */ void createEntry(int hash, K key, V value, int bucketIndex) HashMap.Entry old = tablebucketIndex; E

23、ntry e = new Entry(hash, key, value, old); tablebucketIndex = e; e.addBefore(header); size+; HashMap的put方法,调用了addEntry方法;HashMap的addEntry方法又调用了createEntry方法。因此可以把上面的两个方法和HashMap中的内容放到一起,方便分析,形成如下方法:void addEntry(int hash, K key, V value, int bucketIndex) if (size = threshold) & (null != tablebucketI

24、ndex) resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); HashMap.Entry old = tablebucketIndex; Entry e = new Entry(hash, key, value, old); tablebucketIndex = e; e.addBefore(header); size+; / Remove eldest entry if instructed Entry eldest = hea

25、der.after; if (removeEldestEntry(eldest) removeEntryForKey(eldest.key); 同样,先判断是否超出阈值,超出则增加数组的个数。然后创建Entry对象,并加入到HashMap对应的数组和链表中。与HashMap不同的是LinkedHashMap增加了e.addBefore(header);和removeEntryForKey(eldest.key);这样两个操作。首先分析一下e.addBefore(header)。其中e是LinkedHashMap.Entry对象,addBefore代码如下,作用就是讲header与当前对象相关联

26、,使当前对象增加到header的双向链表的尾部(header.before):private void addBefore(Entry existingEntry) after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; 其次是另一个重点,代码如下: / Remove eldest entry if instructed Entry eldest = header.after; if (removeEldestEntry(eldest) removeEntry

27、ForKey(eldest.key); 其中,removeEldestEntry判断是否需要删除最近最不常使用的那个节点。LinkedHashMap中的removeEldestEntry(eldest)方法永远返回false,如果我们要实现LRU算法,就需要重写这个方法,判断在什么情况下,删除最近最不常使用的节点。removeEntryForKey的作用就是将key对应的节点在HashMap的数组加链表结构中删除,源码如下:final Entry removeEntryForKey(Object key) if (size = 0) return null; int hash = (key =

28、 null) ? 0 : hash(key); int i = indexFor(hash, table.length); Entry prev = tablei; Entry e = prev; while (e != null) Entry next = e.next; Object k; if (e.hash = hash & (k = e.key) = key | (key != null & key.equals(k) modCount+; size-; if (prev = e) tablei = next; else prev.next = next; e.recordRemov

29、al(this); return e; prev = e; e = next; return e; removeEntryForKey是HashMap的方法,对LinkedHashMap中header的双向链表无能为力,而LinkedHashMap又没有重写这个方法,那header的双向链表要如何处理呢。仔细看一下代码,可以看到在成功删除了HashMap中的节点后,调用了e.recordRemoval(this);方法。这个方法在HashMap中为空,LinkedHashMap的Entry则实现了这个方法。其中remove()方法中的两行代码为双向链表中删除当前节点的标准代码,不解释。 /* * Removes this entry from the linked list. */ private void remove() before.after = after; after.before = before; void recordRemoval(HashMap m) remove(); 以上,LinkedHashMap增加节点的代码分析完毕,可以看到完美的将新增的节点放在了header双向链表的末尾。但是,这样显然是

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1