자바 용기 류 소스 분석 - LinkedHashMap

12602 단어 자바
링크 드 하 쉬 맵 은 하 쉬 맵 과 유사 하지만 반복 해서 옮 겨 다 닐 때 '키 쌍' 을 얻 는 순 서 는 삽입 순서 나 최근 최소 (LRU) 를 사용 하 는 순서 입 니 다.HashMap 보다 조금 느 립 니 다.교체 방문 시 오히려 빠 릅 니 다. 링크 를 사용 하여 내부 순 서 를 유지 하기 때 문 입 니 다 (HashMap 은 산 목록 을 바탕 으로 이 루어 집 니 다.
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

링크 드 하 쉬 맵 은 하 쉬 맵 을 계승 하여 맵 인 터 페 이 스 를 실현 했다.
     LinkedHashMap 은 두 개의 속성 만 정의 합 니 다:
1 /**
 2      * The head of the doubly linked list.
 3      *         
 4      */
 5     private transient Entry<K,V> header;
 6     /**
 7      * The iteration ordering method for this linked hash map: true
 8      * for access-order, false for insertion-order.
 9      * true          ,false      
10      */
11     private final boolean accessOrder;

링크 드 리스트 는 모두 다섯 가지 구조 방법 을 제공 했다.
1 //     1,                、       LinkedList
 2 public LinkedHashMap(int initialCapacity, float loadFactor) {
 3     super(initialCapacity, loadFactor);
 4     accessOrder = false;
 5 }
 6 //     2,           LinkedHashMap,             
 7 public LinkedHashMap(int initialCapacity) {
 8     super(initialCapacity);
 9     accessOrder = false;
10 }
11 //     3,                  LinkedHashMap,             
12 public LinkedHashMap() {
13     super();
14     accessOrder = false;
15 }
16 //     4,     map    LinkedHashMap,       (16) (map.zise()/DEFAULT_LOAD_FACTORY)+1    ,        
17 public LinkedHashMap(Map<? extends K, ? extends V> m) {
18     super(m);
19     accessOrder = false;
20 }
21 //     5,      、                LinkedHashMap
22 public LinkedHashMap(int initialCapacity,
23              float loadFactor,
24                          boolean accessOrder) {
25     super(initialCapacity, loadFactor);
26     this.accessOrder = accessOrder;
27 }

구조 방법 에서 알 수 있 듯 이 기본적으로 삽입 순 서 를 사용 하여 키 값 을 추출 하 는 순 서 를 유지 합 니 다. 모든 구조 방법 은 부모 류 의 구조 방법 을 호출 하여 대상 을 만 듭 니 다.
     링크 드 HashMap 은 양 방향 링크 를 바탕 으로 하 는 것 이 고 속성 에 header 노드 가 정 해 져 있 습 니 다. 왜 구조 방법 이 초기 화 되 지 않 았 습 니까?
     링크 드 하 쉬 맵 에는 init () 방법 이 있 습 니 다. 하 쉬 맵 의 구조 방법 은 모두 init () 방법 을 사용 합 니 다. 여기 서 링크 드 하 쉬 맵 의 구조 방법 은 부모 구조 방법 을 호출 한 후에 부모 구조 방법 에서 init () 방법 을 호출 합 니 다.
1 void init() {
2         header = new Entry<K,V>(-1, null, null, null);
3         header.before = header.after = header;
4 }

init () 방법 을 보면 header 를 초기 화하 고 양 방향 순환 링크 를 만 들 었 습 니 다 (링크 드 List 의 저장 구조 와 같 습 니 다).
     transfer (HashMap. Entry [] new Table) 방법 은 init () 방법 과 마찬가지 로 HashTable 에서 도 호출 됩 니 다. transfer (HashMap. Entry [] new Table) 방법 은 HashMap 에서 resize (int new Capacity) 방법 을 호출 할 때 호출 됩 니 다.
1 void transfer(HashMap.Entry[] newTable) {
2     int newCapacity = newTable.length;
3     for (Entry<K,V> e = header.after; e != header; e = e.after) {
4         int index = indexFor(e.hash, newCapacity);
5         e.next = newTable[index];
6         newTable[index] = e;
7     }
8 }

링크 노드 e 의 해시 값 에 따라 e 가 새로운 용량 의 table 배열 에 있 는 색인 을 계산 하고 e 를 계산 한 색인 에 참조 하 는 링크 에 삽입 합 니 다.
     containsValue(Object value)
1 public boolean containsValue(Object value) {
 2     // Overridden to take advantage of faster iterator
 3     if (value==null) {
 4         for (Entry e = header.after; e != header; e = e.after)
 5             if (e.value==null)
 6                 return true;
 7     } else {
 8         for (Entry e = header.after; e != header; e = e.after)
 9                 if (value.equals(e.value))
10                     return true;
11     }
12     return false;
13 }

부모 클래스 의 contains Value (Object value) 방법 을 다시 쓰 고 header 를 통 해 링크 를 옮 겨 다 니 며 table 배열 을 조회 하지 않 고 값 과 value 가 같은 지 판단 합 니 다.
     get(Object key)
1 public V get(Object key) {
2     Entry<K,V> e = (Entry<K,V>)getEntry(key);
3     if (e == null)
4         return null;
5     e.recordAccess(this);
6     return e.value;
7 }

get (Object key) 방법 은 HashMap 의 getEntry (Object key) 방법 으로 노드 를 가 져 오고 이 노드 의 value 값 을 되 돌려 줍 니 다. 노드 가 null 이면 null 로 되 돌려 줍 니 다. recordAccess (HashMap < K, V > m) 는 LinkedHashMap 의 내부 클래스 Entry 를 소개 하 는 방법 입 니 다.
     clear()
1 public void clear() {
2     super.clear();
3     header.before = header.after = header;
4 }

clear () 방법 은 부모 클래스 의 방법 clear () 방법 을 먼저 호출 한 다음 에 링크 의 header 노드 의 before 와 after 인용 을 모두 header 자신, 즉 header 노드 는 양 방향 순환 링크 입 니 다. 그러면 원래 링크 의 나머지 노드 에 접근 할 수 없습니다. 그들 은 모두 GC 에 의 해 회 수 됩 니 다.
     이상 의 내용 은 많 고 적 음 은 모두 LinkedHashMap 의 내부 류 Entry < K, V > 와 관련 되 어 있 으 며, 아래 에 이 내부 류 를 상세 하 게 소개 합 니 다.
1 //        、      ,   HashMap Entry。
 2 private static class Entry<K,V> extends HashMap.Entry<K,V> {
 3         //         
 4         Entry<K,V> before, after;
 5     //                
 6     Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
 7         super(hash, key, value, next);
 8     }
 9 
10     //      ,         after        before  
11 private void remove() {
12     //             ,  GC  
13         before.after = after;
14         after.before = before;
15     }
16 
17     //              (           )
18 private void addBefore(Entry<K,V> existingEntry) {
19     //       after    existingEntry
20         after  = existingEntry;
21         //  before     existingEntry       
22 before = existingEntry.before;
23         //    existingEntry        after        
24 before.after = this; 
25         //    existingEntry        before        
26         after.before = this;
27     }
28 
29 //       get   put  (put        HashMap.Entry put
30 //   )     recordAccess(HashMap<K,V> m)  
31 //   accessOrder true,              ,        
32 //      header    ,      。
33 //        HashMap.Entry      
34 // recordAccess(HashMap<K,V> m)     
35     void recordAccess(HashMap<K,V> m) {
36         LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
37         if (lm.accessOrder) {
38             lm.modCount++;
39             remove();
40             addBefore(lm.header);
41         }
42     }
43     //  recordAccess(HashMap<K.V> m)    , HashMap.Entry            。     (remove)         
44     void recordRemoval(HashMap<K,V> m) {
45         remove();
46     }
47 }

내부 클래스 Entry 를 소 개 했 습 니 다. 다음은 Entry 노드 를 만 들 고 Entry 를 추가 하 는 두 가지 방법 입 니 다.
     createEntry(int hash,K key,V value,int bucketIndex)
1 void createEntry(int hash, K key, V value, int bucketIndex) {
2     HashMap.Entry<K,V> old = table[bucketIndex];
3     Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
4     table[bucketIndex] = e;
5     e.addBefore(header);
6     size++;
7 }

createEntry (int hash, K key, V value, int bucketIndex) 방법 은 부모 클래스 HashMap 의 방법 을 덮어 씁 니 다. 이 방법 은 table 배열 의 크기 를 넓 히 지 않 습 니 다. 이 방법 은 table 에 있 는 bucketIndex 의 노드 를 먼저 보존 한 다음 Entry 의 구조 방법 (부모 클래스 HashMap. Entry 로 호출 되 는 구조 방법) 을 사용 하여 노드 를 추가 합 니 다. 현재 노드 의 next 참조 지향 table[bucketIndex] 의 노드, 그 다음 에 호출 된 e. addBefore (header) 는 링크 를 수정 하고 e 노드 를 header 노드 에 추가 합 니 다.
     이 방법 은 table [bucketIndex] 의 링크 에 노드 를 추가 하고 링크 드 HashMap 자체 의 링크 에 노드 를 추가 합 니 다.
     addEntry(int hash, K key, V value, int bucketIndex)
1 void addEntry(int hash, K key, V value, int bucketIndex) {
 2     createEntry(hash, key, value, bucketIndex);
 3     Entry<K,V> eldest = header.after;
 4     if (removeEldestEntry(eldest)) {
 5         removeEntryForKey(eldest.key);
 6     } else {
 7         if (size >= threshold)
 8             resize(2 * table.length);
 9     }
10 }

먼저 createEntry (int hash, K key, V value, int bucketIndex) 방법 을 호출 한 다음 LinkedHashMap 에서 '가장 오래된' (최근 최소 사용) 노드 를 가 져 옵 니 다. 이 어 removeEldestEntry (Entry < K, V > eldest) 방법 과 관련 되 어 있 습 니 다.
1 protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
2     return false;
3 }

왜 이 방법 은 처음부터 끝까지 false 로 돌아 갑 니까?
     위의 addEntry (int hash, K key, V value, int bucketIndex) 방법 을 결합 하면 링크 드 HashMap 을 정상 적 인 Map 으로 만 들 고 '가장 오래된' 노드 를 제거 하지 않 습 니 다.
     왜 코드 에서 이 부분의 논 리 를 직접 제거 하지 않 고 이렇게 설계 되 었 습 니까?
     이것 은 개발 자 에 게 편 의 를 제공 합 니 다. 만약 에 맵 을 Cache 로 사용 하고 크기 를 제한 하려 면 LinkedHashMap 을 계승 하고 removeEldestEntry (Entry < K, V > eldest) 방법 을 다시 쓰 십시오. 이렇게:
1 private static final int MAX_ENTRIES = 100;
2 protected boolean removeEldestEntry(Map.Entry eldest) {
3      return size() > MAX_ENTRIES;
4 }

  링크 드 하 쉬 맵 은 상기 내용 을 제외 하고 교체 와 관련 된 세 가지 방법 과 세 가지 내부 유형 과 추상 적 인 내부 유형 이 있 는데 그것 이 바로 new Key Iterator (), new Value Iterator (), new Entry Iterator () 와 Key Iterator 류, Value Iterator 류, Entry Iterator 류 와 링크 드 하 쉬 Iterator 류 이다.
     세 가지 new 방법 은 각각 대응 하 는 세 가지 인 스 턴 스 를 되 돌려 줍 니 다. 세 가지 유형 은 모두 추상 적 인 링크 드 Hash Iterator 에서 계승 합 니 다. 다음은 교체 와 관련 된 세 가지 유형 을 보 겠 습 니 다.
1 private class KeyIterator extends LinkedHashIterator<K> {
2     public K next() { return nextEntry().getKey(); }
3 }
4 private class ValueIterator extends LinkedHashIterator<V> {
5     public V next() { return nextEntry().value; }
6 }
7 private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
8     public Map.Entry<K,V> next() { return nextEntry(); }
9 }

위 에서 알 수 있 듯 이 이 세 가지 유형 은 모두 간단 합 니 다. 하나의 next () 방법 만 있 습 니 다. next () 방법 도 링크 드 Hash Iterator 류 에 해당 하 는 방법 을 호출 하 는 것 입 니 다. 키 Iterator 류, Value Iterator 류, Entry Iterator 류 와 링크 드 Hash Iterator 류 를 호출 하 는 것 입 니 다.
     다음은 링크 드 Hash Iterator 류 의 내용 입 니 다.
 1 private abstract class LinkedHashIterator<T> implements Iterator<T> {
 2     Entry<K,V> nextEntry    = header.after;
 3     Entry<K,V> lastReturned = null;
 4     //  LinkedList ListItr    expectedModCount    
 5     int expectedModCount = modCount;
 6     //         header                  ,         ,        
 7     public boolean hasNext() {
 8         return nextEntry != header;
 9     }
10     //           lastReturned 
11     public void remove() {
12         if (lastReturned == null)
13             throw new IllegalStateException();
14         if (modCount != expectedModCount)
15             throw new ConcurrentModificationException();
16         LinkedHashMap.this.remove(lastReturned.key);
17         lastReturned = null;
18         expectedModCount = modCount;
19     }
20     //        
21     Entry<K,V> nextEntry() {
22         if (modCount != expectedModCount)
23             throw new ConcurrentModificationException();
24         if (nextEntry == header)
25             throw new NoSuchElementException();
26         //           
27         Entry<K,V> e = lastReturned = nextEntry;
28         //            
29         nextEntry = e.after;
30         return e;
31     }
32 }

LinkedHashMap 은 HashMap 및 LinkedList 와 함께 분석 하여 그들의 공통점 과 차이 점 을 비교 해 야 합 니 다. 보완 하기 위해 서 는 그들 간 의 공통점 과 차이 점 을 간단하게 정리 해 야 합 니 다.
     HashMap 은 해시 표를 사용 하여 데 이 터 를 저장 하고 지퍼 법 으로 충돌 을 처리 합 니 다. LinkedHashMap 은 HashMap 에서 계승 하 는 동시에 자체 에 하나의 링크 가 있 습 니 다. 링크 를 사용 하여 데 이 터 를 저장 합 니 다. 충돌 이 없습니다. LinkedList 는 LinkedHashMap 과 마찬가지 로 양 방향 순환 링크 를 사용 하지만 간단 한 데 이 터 를 저장 합 니 다. "키 쌍"이 아 닙 니 다.. 그래서 HashMap 과 LinkedHashMap 은 Map 이 고 LinkedList 는 List 입 니 다. 이것 은 그들의 본질 적 인 차이 입 니 다. LinkedList 와 LinkedHashMap 은 모두 내용 의 순 서 를 유지 할 수 있 지만 HashMap 은 순 서 를 유지 하지 않 습 니 다.

좋은 웹페이지 즐겨찾기