HashMapのとのLinkedHashMapソース上のいくつかの結論
場合下地JDK1.8のHashMap後の構造、アレイ(ノード<K、V>テーブル)と長さ64より大きい場合、リストは赤黒木に変換される8、鎖長以上の長さの鎖(静止ノード) 6時00分以下で赤黒木に変換されないであろう。目的は、効率性を確保することです。(二重リストをリンクされた定義された)後、前記リンクされたリストのノードは、次に、エントリ内のLinkedHashMap <K、V>は、前に添加順次堆積横断するとき、反復は、保証することができます。
以下は、定義の二重リンクリストであるのLinkedHashMap
//HashMap方法
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
//LinkedHashMap----------------------------------------
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);
}
}
//添加结点,添加次序为从右到左,移动last指针
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}
アクセス(取得)の順序を変更することができ、ブール値のLinkedHashMap accessOrderがあり、デフォルトでは、尾にアクセスするために、真のキーを入れて、変更しない偽で、それはLRUへの鍵であり、この値は、コンストラクタで得ることができます。
0.75Fデフォルトの負荷係数、容量よりも大きいユーザ定義のサイズは* 0.75(閾値の体積)、アレイは、拡張のために、負荷は、あまりにも簡単ハッシュ衝突小さすぎてはならないされ、スペースの無駄が小さすぎると
デフォルトはfalseを返します、それはソースノード除去ヘッド場合は、次のが真の実行:removeEldestEntry方法について
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
//拿到最右边key - 最早添加的数据key
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
- しかし、二人はスレッドセーフプラスノー同期ロック(同期)ではない、とハッシュテーブルは安全です
HashMapのは、LRUを達成します
- 主なアイデアは、次のとおりです。多くの場合、複数使用して、ヒット、そしてちょうどヘッドを移動するために追加、最初のバッファのうち尾がいっぱいです
- 最近最も使用 - これはノードを意味し、また、我々はインデックスが交換を考慮していないと同じようにノード、キーを変更する必要があります
- 頭と尾のためにどのようなデータが含まれていない接合ノードを、削除
ノードの初期化
//定义双列集合的结点结构(双向链表的结点)
private class Node {
//key的作用是cache满的时候,hashmap便于淘汰尾部和移除操作,还有遍历
int key;
int value;
Node pre;
Node post;
//构造方法初始化Node数据
public Node(int key, int value) {
this.key = key;
this.value = value;
}
public Node() {
}
}
メンバ変数定義されたキャッシュ
//定义头尾结点
private Node head;
private Node tail;
//定义当前缓存大小
private int count;
//定义总缓存大小
private int size;
//定义双列集合存储数据
private HashMap<Integer, Node> cache;
//构造方法初始化数据
public LRUCache(int size) {
//双向链表初始化
head = new Node();
tail = new Node();
//结点外的指针置空
head.pre = null;
tail.post = null;
//头尾结点的互连
head.post = tail;
tail.pre = head;
//容量初始化
this.count = 0;
this.size = size;
cache = new HashMap<>(size);
}
アクセスキャッシュされたコンテンツの方法
//get方法得到key中缓存的数据
public int get(int key) {
//取得hashmap中的结点数据
Node node = cache.get(key);
//如果没有返回-1
if (node == null)
return -1;
//有,访问后将结点移动到开头,成为最近使用结点
moveToHead(node);
//并返回查询的值
return node.value;
}
現在のノードがヘッドノードの前に移動した後、
//摘除双链表结点
private void removeNode(Node node) {
node.pre.post = node.post;
node.post.pre = node.pre;
}
//结点插入头部之后
private void insertNode(Node node) {
//前连插入结点
node.pre = head;
node.post = head.post;
//后连插入结点
head.post.pre = node;
head.post = node;
}
private void moveToHead(Node node) {
//摘除结点
removeNode(node);
//插入头结点之后
insertNode(node);
}
メソッドにチューニング
//put方法存入数据,同时将值放入hashmap的node
public void put(int key, int value) {
//获取Node仓库
Node node = cache.get(key);
//如果没有命中就调进
if (node == null) {
//如果cache满了淘汰尾部
if (count >= size) {
cache.remove(tail.pre.key);
//摘除tail尾部前一个结点
removeNode(tail.pre);
//数量--
count--;
}
Node newNode = new Node(key, value);
cache.put(key, newNode);
//由于刚添加,把新数据结点移动到头部
insertNode(newNode);
count++;
}
//如果命中更新该key索引的node值,并移至开头
else {
node.value = value;
//如果目前只有一个节点不用摘
if(count == 1) {
return;
}
moveToHead(node);
}
}
要素の一つを除去する方法
//移除缓存中的一个数据
public void remove(int key) {
Node node = cache.get(key);
//没有就什么也不干,有就删除
if (node == null)
return;
cache.remove(key);
removeNode(node);
}
LRUを達成するためのLinkedHashMap
package cn.work.demo.demo02;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
public class LRULinkedHashMapCache<K,V> extends LinkedHashMap<K,V> {
//Cache容量
private int size;
//定义一个锁保证线程安全
private final ReentrantLock lock = new ReentrantLock();
//初始化,当参数accessOrder为true时,即会按照访问顺序排序,最近访问的放在尾部,最早访问的放在头部(数据由last添加至尾部)
public LRULinkedHashMapCache(int size) {
super(size,0.75f,true);
this.size = size;
}
//重写removeEldestEntry方法,若当前容量>size,弹出尾部
@Override
public boolean removeEldestEntry(Map.Entry<K,V> eldest) {
//size()方法,每当LinkedHashMap添加元素时就会++
return size() > size;
}
//重写LinkedHashMap的方法,加锁保证线程安全
@Override
public V put(K key, V value) {
try {
lock.lock();
return super.put(key, value);
} finally {
lock.unlock();
}
}
@Override
public V get(Object key) {
try {
lock.lock();
return super.get(key);
} finally {
lock.unlock();
}
}
@Override
public V remove(Object key) {
try {
lock.lock();
return super.remove(key);
} finally {
lock.unlock();
}
}
}
完全なコード
HashMapの
package cn.work.demo.demo02;
import java.util.*;
//主要思路是:常命中(使用多)和刚添加的移至头部,缓存满了先淘汰尾部
//最近最久未使用 - 指的是Node里的数据,需要变换的也是Node,key只作为索引不考虑交换
//tail和head结点用于摘除结点,其中并不包含任何数据
public class LRUCache {
//定义双列集合的结点结构(双向链表的结点)
private class Node {
//key的作用是cache满的时候,hashmap便于淘汰尾部和移除操作,还有遍历
int key;
int value;
Node pre;
Node post;
//构造方法初始化Node数据
public Node(int key, int value) {
this.key = key;
this.value = value;
}
public Node() {
}
}
//定义头尾结点
private Node head;
private Node tail;
//定义当前缓存大小
private int count;
//定义总缓存大小
private int size;
//定义双列集合存储数据
private HashMap<Integer, Node> cache;
//构造方法初始化数据
public LRUCache(int size) {
//双向链表初始化
head = new Node();
tail = new Node();
//结点外的指针置空
head.pre = null;
tail.post = null;
//头尾结点的互连
head.post = tail;
tail.pre = head;
//容量初始化
this.count = 0;
this.size = size;
cache = new HashMap<>(size);
}
//get方法得到key中缓存的数据
public int get(int key) {
//取得hashmap中的结点数据
Node node = cache.get(key);
//如果没有返回-1
if (node == null)
return -1;
//有,访问后将结点移动到开头,成为最近使用结点
moveToHead(node);
//并返回查询的值
return node.value;
}
//摘除双链表结点
private void removeNode(Node node) {
node.pre.post = node.post;
node.post.pre = node.pre;
}
//结点插入头部之后
private void insertNode(Node node) {
//前连插入结点
node.pre = head;
node.post = head.post;
//后连插入结点
head.post.pre = node;
head.post = node;
}
private void moveToHead(Node node) {
//摘除结点
removeNode(node);
//插入头结点之后
insertNode(node);
}
//put方法存入数据,同时将值放入hashmap的node
public void put(int key, int value) {
//获取Node仓库
Node node = cache.get(key);
//如果没有命中就调进
if (node == null) {
//如果cache满了淘汰尾部
if (count >= size) {
cache.remove(tail.pre.key);
//摘除tail尾部前一个结点
removeNode(tail.pre);
//数量--
count--;
}
Node newNode = new Node(key, value);
cache.put(key, newNode);
//由于刚添加,把新数据结点移动到头部
insertNode(newNode);
count++;
}
//如果命中更新该key索引的node值,并移至开头
else {
node.value = value;
//如果目前只有一个节点不用摘
if (count == 1) {
return;
}
moveToHead(node);
}
}
//移除缓存中的一个数据
public void remove(int key) {
Node node = cache.get(key);
//没有就什么也不干,有就删除
if (node == null)
return;
cache.remove(key);
removeNode(node);
}
//用于遍历cache
public void print() {
Set<Integer> keyset = cache.keySet();
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
int key = (int) iterator.next();
System.out.println(cache.get(key).key + "-->" + cache.get(key).value);
}
}
}
LinkedHashMap
package cn.work.demo.demo02;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
public class LRULinkedHashMapCache<K,V> extends LinkedHashMap<K,V> {
//Cache容量
private int size;
//定义一个锁保证线程安全
private final ReentrantLock lock = new ReentrantLock();
public LRULinkedHashMapCache(int size) {
super(size,0.75f,true);
this.size = size;
}
//初始化,当参数accessOrder为true时,即会按照访问顺序排序,最近访问的放在最前,最早访问的放在后面
//重写removeEldestEntry方法,若当前容量>size,弹出尾部
@Override
public boolean removeEldestEntry(Map.Entry<K,V> eldest) {
//size()方法,每当LinkedHashMap添加元素时就会++
return size() > size;
}
//重写LinkedHashMap的方法,加锁保证线程安全
@Override
public V put(K key, V value) {
try {
lock.lock();
return super.put(key, value);
} finally {
lock.unlock();
}
}
@Override
public V get(Object key) {
try {
lock.lock();
return super.get(key);
} finally {
lock.unlock();
}
}
@Override
public V remove(Object key) {
try {
lock.lock();
return super.remove(key);
} finally {
lock.unlock();
}
}
}
Mainメソッド
package cn.work.demo.demo02;
import java.util.Map;
public class LRU {
public static void main(String[] args) {
LRUCache lru = new LRUCache(4);
lru.put(1,2);
lru.put(2,5);
lru.put(8,10);
lru.put(6,5);
lru.get(1);
lru.put(3,8);
lru.get(8);
lru.put(5,2);
lru.put(6,2);
lru.put(7,2);
lru.print();
System.out.println("//-------------------------------");
LRULinkedHashMap<Integer,Integer> linkedHashMap= new LRULinkedHashMap<>(4);
linkedHashMap.put(1,2);
linkedHashMap.put(2,5);
linkedHashMap.put(8,10);
linkedHashMap.put(6,5);
linkedHashMap.get(1);
linkedHashMap.put(3,8);
linkedHashMap.get(8);
linkedHashMap.put(5,2);
linkedHashMap.put(6,2);
linkedHashMap.put(7,2);
for (Map.Entry<Integer,Integer> entry:linkedHashMap.entrySet()){
System.out.println(entry.getKey()+"-->"+entry.getValue());
}
}
}
結果
8 - > 10
5 - > 2
6 - > 2
7 - > 2
// ---------------------------- ---
8 - > 10
5 - > 2
6 - > 2
7 - > 2