LinkedHashMap 최고의 실천: LruCache

한 마디 로 루 캐 시 (least recently used cache) 는 최근 캐 시 를 최소 화 했다.    앞에서 우 리 는 링크 드 하 쉬 맵 데이터 구 조 를 함께 배 웠 습 니 다. 그러면 LruCache 는 링크 드 하 쉬 맵 의 가장 좋 은 실천 입 니 다. 어린이 신발 들 은 제 블 로그 선형 표 데이터 구조 해석 (6) 체인 하 쉬 표 구조 - 링크 드 하 쉬 맵 을 보고 배 울 수 있 습 니 다.    일상적인 개발 에서 우 리 는 부 드 러 운 인용 이나 약 한 인용 (SoftReference or WeakReference) 이라는 메모리 캐 시 기술 을 자주 사용한다.하지만 이 제 는 이런 방식 을 추천 하지 않 는 다. 안 드 로 이 드 2.3 (API Level 9) 부터 쓰레기 수 거 기 는 소프트 인용 이나 약 한 인용 을 가 진 대상 을 회수 하 는 경향 이 강해 소프트 인용 과 약 한 인용 을 신뢰 할 수 없 게 만 들 기 때문이다.또한, Android 3.0 (API Level 11) 에 서 는 그림 의 데이터 가 로 컬 메모리 에 저장 되 기 때문에 예측 가능 한 방식 으로 방출 할 수 없습니다. 이 는 잠재 적 인 위험 으로 인해 프로그램의 메모리 가 넘 치고 붕 괴 될 수 있 습 니 다.    구 글 은 대개 SDK 21 부터 LruCache 라 는 도구 류 (안 드 로 이 드 - 슈퍼 포트 - v4 패키지 에서 제공) 를 제공 하여 메모리 캐 시 기술 을 실현 하 는 솔 루 션 으로 사용 합 니 다.이 종 류 는 그림 을 캐 시 하 는 데 매우 적합 합 니 다. 주요 알고리즘 원 리 는 최근 에 사용 한 대상 을 링크 드 HashMap 에 강 한 인용 으로 저장 하고 최근 에 가장 적 게 사용 한 대상 을 캐 시 값 이 미리 설정 되 기 전에 메모리 에서 제거 하 는 것 입 니 다.
소스 코드 해독
    OK 옛 규칙, 제 가 먼저 여러분 을 데 리 고 LruCache 의 소스 코드 를 연구 하 겠 습 니 다. 우 리 는 get, put, Remove 등 방법 을 중점적으로 보 겠 습 니 다. 사실은 원 리 는 바로 LinkedHashMap 의 체제 입 니 다.
public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;//     LinkedHashMap
    private int size;//          
    private int maxSize;//          
    private int putCount;// put   
    private int createCount;// create   
    private int evictionCount;//      
    private int hitCount;//      
    private int missCount;//      
    /** *        LruCache     * @param maxSize for caches that do not override {@link #sizeOf}, this is * the maximum number of entries in the cache. For all other caches, * this is the maximum sum of the sizes of the entries in this cache. */
    public LruCache(int maxSize) {//     maxSize          1/8
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }
    /** * Sets the size of the cache. * @param maxSize The new maximum size. */
    public void resize(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        synchronized (this) {
            this.maxSize = maxSize;
        }
        trimToSize(maxSize);
    }
    /** * Returns the value for {@code key} if it exists in the cache or can be * created by {@code #create}. If a value was returned, it is moved to the * head of the queue. This returns null if a value is not cached and cannot * be created. */
    public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V mapValue;
        synchronized (this) {
            mapValue = map.get(key);
            if (mapValue != null) {
                hitCount++;
                return mapValue;
            }
            missCount++;
        }

        /* * Attempt to create a value. This may take a long time, and the map * may be different when create() returns. If a conflicting value was * added to the map while create() was working, we leave that value in * the map and release the created value. */

        V createdValue = create(key);
        if (createdValue == null) {
            return null;
        }

        synchronized (this) {
            createCount++;
            mapValue = map.put(key, createdValue);

            if (mapValue != null) {
                // There was a conflict so undo that last put
                map.put(key, mapValue);
            } else {
                size += safeSizeOf(key, createdValue);
            }
        }

        if (mapValue != null) {
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            trimToSize(maxSize);
            return createdValue;
        }
    }

    /** * Caches {@code value} for {@code key}. The value is moved to the head of * the queue. * @return the previous value mapped by {@code 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++;
            size += safeSizeOf(key, value);
            previous = map.put(key, value);
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            //          
            entryRemoved(false, key, previous, value);
        }
        //   
        trimToSize(maxSize);
        return previous;
    }

    /** *        ,                     * Remove the eldest entries until the total of remaining entries is at or * below the requested size. * @param maxSize the maximum size of the cache before returning. May be -1 * to evict even 0-sized elements. */
    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 || map.isEmpty()) {
                    break;
                }

                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }

    /** *            * Removes the entry for {@code key} if it exists. * @return the previous value mapped by {@code key}. */
    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;
    }
    ……
    }

위 에 있 는 LruCache 초기 화 할당 캐 시 크기 가 얼마나 되 는 지 아래 의 몇 가지 요 소 를 참고 할 수 있 습 니 다.
  • 당신 의 장 치 는 모든 프로그램 에 얼마나 큰 메모 리 를 분배 할 수 있 습 니까?
  • 장치 화면 에 최대 몇 장의 그림 이 표 시 됩 니까?화면 에 도 곧 표 시 될 수 있 기 때문에 미리 불 러 와 야 할 그림 이 얼마나 있 습 니까?
  • 장치 의 화면 크기 와 해상 도 는 각각 얼마 입 니까?
  • 그림 의 크기 와 크기, 그리고 그림 마다 몇 개의 메모리 공간 을 차지 합 니까?
  • 사진 이 방문 하 는 빈 도 는 얼마나 됩 니까?일부 그림 의 접근 빈도 가 다른 그림 보다 높 지 않 습 니까?있 으 면 메모리 에 그림 을 상주 시 키 거나 여러 개의 LruCache 대상 을 사용 하여 다른 그룹의 그림 을 구분 해 야 할 수도 있 습 니 다.

  • 기본 사용
        Cache 는 콘 텐 츠 의 수 를 제한 하기 위해 강 한 인용 을 저장 합 니 다. 아 이 템 이 방문 할 때마다 이 아 이 템 은 대기 열의 머리 로 이동 합 니 다.cache 가 가득 찼 을 때 새 아 이 템 을 추가 하면 대기 열 끝 에 있 는 아 이 템 을 회수 합 니 다.
    int cacheSize = 4 * 1024 * 1024; // 4MiB
       LruCache bitmapCache = new LruCache(cacheSize) {
           protected int sizeOf(String key, Bitmap value) {
               return value.getByteCount();
    
       }
      }

        그림 을 저장 할 4M 크기 의 저장 공간 을 만 듭 니 다. 대기 열 형식 으로 저장 한 다음 에 저 장 된 것 과 최근 에 사 용 된 것 은 대기 열의 마지막 에 놓 습 니 다. 이렇게 오래된 데 이 터 는 대기 열의 시작 에 놓 여 GC 회수 에 사 용 됩 니 다.
       synchronized (cache) {
         if (cache.get(key) == null) { 
             cache.put(key, value);
    
       }}

        이 방법 은 LruCache 가 저장 한 데 이 터 를 어떻게 규범화 하고 가 져 오 는 지 보 여 줍 니 다. 이 종 류 는 스 레 드 가 안전 하기 때문에 동기 블록 을 추가 하여 데 이 터 를 저장 하고 get 과 put 방식 으로 데 이 터 를 액세스 해 야 합 니 다. 이 점 은 Map 과 일치 합 니 다. put 시 키 가 같 으 면 데 이 터 를 덮어 씁 니 다.하지만 여기 키 와 value 가 비어 있 으 면 안 되 는 점 을 주의해 야 합 니 다. 여 기 는 맵 과 차이 가 있 습 니 다.    또한 자원 을 주동 적 으로 방출 해 야 합 니 다. 만약 에 cache 의 특정한 값 이 명확 하 게 방출 되 고 재 작성 방법 이 필요 하 다 면 주의해 야 합 니 다.
    entryRemoved (boolean evicted, K key, V oldValue, V newValue)

        자원 이 시스템 에서 회수 되 었 다 면 evicted 는 TRUE 로 되 돌아 갑 니 다. put, remove 방식 으로 회수 되 었 다 면 evicted 는 FALSE 로 되 돌아 갑 니 다. 그 다음 에 put 를 통 해 인지 reove 를 통 해 인지, new Value 가 비어 있 는 지 여 부 를 판단 할 수 있 습 니 다. 비어 있 으 면 put 호출 한 다음 에 reove 와 시스템 을 회수 할 때 자원 을 비 워 두 면 스스로 실현 해 야 합 니 다.    키 에 해당 하 는 아 이 템 을 잃 어 버 리 면 create () 를 다시 씁 니 다. 호출 코드 를 간소화 하고 잃 어 버 려 도 되 돌아 갑 니 다.기본 cache 크기 는 측정 한 item 의 수량 입 니 다. size of 를 다시 써 서 서로 다른 item 의 크기 를 계산 합 니 다.
    참조 링크:http://blog.csdn.net/linghu_java/article/details/8574102

    좋은 웹페이지 즐겨찾기