LinkedHashMap 및 그 응용 LruCache
52681 단어 데이터 구조
간단 한 소개
링크 드 하 쉬 맵 은 하 쉬 맵 의 하위 클래스 입 니 다.HashMap 을 바탕 으로 양 방향 링크 로 이 루어 졌 습 니 다.다시 말 하면 LinkedHashMap 의 모든 요 소 는 HashMap 의 데이터 구 조 를 만족 시 킬 뿐만 아니 라 LinkedList 의 구 조 를 만족 시 키 고 이 두 가지 실현 방식 을 유지 하고 있다.
링크 드 HashMap 류 는 주로 양 방향 링크 를 유지 하고 다른 작업 은 주로 HashMap 에서 이 루어 집 니 다.
LinkedHashMapEntry
static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> {
//
LinkedHashMapEntry<K,V> before, after;
LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
주요 변수
transient LinkedHashMapEntry<K,V> head; , ;
transient LinkedHashMapEntry<K,V> tail; , ;
final boolean accessOrder; , ; false; LruCache ;
링크 드 하 쉬 맵 은 주로 하 쉬 맵 의 세 가지 빈 방법 을 실현 합 니 다.
// Callbacks to allow LinkedHashMap post-actions
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }
put () 방법
put () 방법 은 HashMap 의 put () 방법 을 호출 하 는 것 입 니까? 주로 putVal () 에서 new Node () 방법 과 차이 가 있 습 니 다. LinkedHashMap 자신의 new Node () 방법 을 호출 합 니 다.다른 것 은 다 똑 같 습 니 다. 마지막 으로 Liked HashMap 방법 이 실현 되 었 습 니 다.
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
//1 LinkedHashMap newNode() ;
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
//2 key ,afterNodeAccess() LinkedHashMap ;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
//3 ,HashMap ,LinkedHashMap ;
afterNodeInsertion(evict);
return null;
}
LinkedHashMap 의 new Node () 방법;
// , , ;
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMapEntry<K,V> p =
new LinkedHashMapEntry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
}
// ;
private void linkNodeLast(LinkedHashMapEntry<K,V> p) {
LinkedHashMapEntry<K,V> last = tail;
// ;
tail = p;
// null, LinkedHashMap ,head null, head ;
if (last == null)
head = p;
//head null, : p before last ,last after p ;
else {
p.before = last;
last.after = p;
}
}
after NodeAccess (): 방문 순서에 따라 정렬 되 고 방문 하 는 노드 가 끝 점 이 아니라면;현재 노드 를 링크 에서 삭제 하고 끝 점 에 삽입 한 후;
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMapEntry<K,V> last;
// , ;
if (accessOrder && (last = tail) != e) {
// e p, b, a;
LinkedHashMapEntry<K,V> p =
(LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
// p after null;
p.after = null;
// b null, , ; null, b after a ;
if (b == null)
head = a;
else
b.after = a;
// , p ,a null, a before b;
if (a != null)
a.before = b;
else
last = b;
// ,last null, p ,last p ,p before last ,last after p ;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
// ;
tail = p;
++modCount;
}
}
after NodeInsertion (): 양 방향 링크 에서 가장 오래된 노드 즉 머리 결산 점 을 삭제 합 니 다.기본적으로 removeEldestEntry () 는 false 를 되 돌려 줍 니 다.LruCache 클래스 는 다시 쓰 고 true 로 돌아 갑 니 다.
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMapEntry<K,V> first;
// put() evict true;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
// HashMap removeNode()
removeNode(hash(key), key, null, false, true);
}
}
get () 방법
get (): 대응 하 는 값 을 가 져 오고 방문 정렬 에 따라 양 방향 링크 에서 현재 노드 e 를 삭제 한 다음 에 링크 끝 노드 에 삽입 합 니 다.양 방향 링크 의 순 서 를 바 꿉 니 다.
public V get(Object key) {
Node<K,V> e;
// HashMap getNode() , null; null,
if ((e = getNode(hash(key), key)) == null)
return null;
// , e, ;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
remove () 방법
reove (): HashMap 의 reove () 방법 을 완전히 호출 하고 삭제 한 후에 after Node Removal () 을 호출 하여 현재 노드 를 양 방향 링크 에서 삭제 합 니 다.
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMapEntry<K,V> p =
(LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
p.before = p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a == null)
tail = b;
else
a.before = b;
}
LruCache
내부 에서 LinkedHashMap 을 구성원 변수 로 저장 요소 로 사용 합 니 다.key, value 는 null 일 수 없습니다.링크 드 HashMap 의 더 블 링크 와 방문 순서 에 따라 정렬 되 는 지 여부 에 따라 이 루어 집 니 다.주로 이미지 로드 프레임 워 크 에 사용 되 며 최근 최소 알고리즘 에 따라 자주 사용 되 지 않 는 그림 을 삭제 하고 공간 을 절약 합 니 다.
초기 화
LruCache<String, String> lruCache = new LruCache<String, String>(10) {
// 1; Bitmap byte;
@Override
protected int sizeOf(String key, String value) {
//super.sizeOf(key,value);
return 2;
}
//
@Override
protected void entryRemoved(boolean evicted, String key, String oldValue, String newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
}
};
put () 방법: key 에 대응 하 는 노드 가 존재 하지 않 습 니 다. 새로운 노드 를 만 들 고 더 블 체인 체인 꼬리 에 추가 합 니 다.존재 하면 값 바 꾸 기;
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++;
// value size, sizeOf() , value int ; ;
size += safeSizeOf(key, value);
// , key , , null;
previous = map.put(key, value);
// null,
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
//entryRemoved , , value;
if (previous != null) {
entryRemoved(false, key, previous, value);
}
// size
trimToSize(maxSize);
return previous;
}
trimToSize () 방법: put () 방법 을 사용 한 후 현재 size 가 maxSize 보다 큰 지 판단 합 니 다. 크 면 최근 에 가장 적 게 사용 한 요 소 를 삭제 합 니 다 (즉, 더 블 체인 헤더 에서 두 노드 를 삭제 합 니 다).
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize) {
break;
}
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
get () 방법: key 에 대응 하 는 노드 가 존재 하고 해당 하 는 value 를 직접 되 돌려 줍 니 다.존재 하지 않 습 니 다. create (key) 방법 을 호출 합 니 다. 이 방법 은 기본적으로 null 로 돌아 갑 니 다.create () 방법 을 만들어 스스로 실현 할 수도 있다.
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
// key value;
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
//create
V createdValue = create(key);
if (createdValue == null) {
return null;
}
//create() null
synchronized (this) {
createCount++;
// , oldVaue;
mapValue = map.put(key, createdValue);
// , create() , value, value
if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue);
// size
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
reove (): key 에 대응 하 는 노드 가 존재 합 니 다. 삭제 하고 size 를 줄 입 니 다.
public final V remove(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V previous;
synchronized (this) {
previous = map.remove(key);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, null);
}
return previous;
}
모든 분석 내용 은 여기 서 끝 났 습 니 다.문제 가 있 으 면 많이 가르쳐 주세요. 감사합니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
정수 반전Udemy 에서 공부 한 것을 중얼거린다 Chapter3【Integer Reversal】 (예) 문자열로 숫자를 반전 (toString, split, reverse, join) 인수의 수치 (n)가 0보다 위 또는 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.