HashMap 의 put 원리 분석.
7179 단어 자바
JDK 1.8 은 주로 문 제 를 해결 하거나 최적화 시 켰 다.
사이즈 확장 최적화
4.567917.빨간색 과 검은색 나 무 를 도 입 했 는데 그 목적 은 단일 링크 가 너무 길 어서 조회 효율 에 영향 을 주지 않도록 하 는 것 이다
4.567917.다 중 스 레 드 순환 문 제 를 해결 하 였 으 나 비 스 레 드 가 안전 하고 다 중 스 레 드 일 때 데이터 손실 문 제 를 초래 할 수 있 습 니 다
먼저 소스 코드 를 보 겠 습 니 다.
1 층 은 puutVal 을 호출 하 는 방법 입 니 다.
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
puutVal 다시 보기:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node[] tab; Node p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node 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)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;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
1.키 값 이 배열 table[i]이 비어 있 거나 길이 가 0 인지 판단 하고,만약 에 resize()를 실행 하여 확장 합 니 다.
2.키 키 키 에 따라 hash 가 삽입 할 수 있 는 배열 색인 i 를 계산 합 니 다.table[i]=null 이 있 으 면 새 노드 를 직접 추가 하고 삽입 에 성공 하면 실제 존재 하 는 키 쌍 을 판단 합 니 다. 수량 size 가 최대 용량 threshold 를 초과 하 였 는 지,초과 하면 확장 합 니 다.table[i]가 비어 있 지 않 으 면.그렇지 않 으 면
3.table[i]의 첫 번 째 요소 가 key 와 같 는 지 판단 합 니 다.같은 값 으로 value 를 직접 덮어 쓰 지 않 으 면 table[i]가 빨 간 검 은 나무 인지,즉 table[i]가 빨 간 검 은 나무 인지 판단 합 니 다.빨 간 검 은 나무 라면 트 리 에 키 값 을 직접 삽입 합 니 다.그렇지 않 으 면 table[i]를 옮 겨 다 니 며 링크 길이 가 8 이상 인지 판단 합 니 다.8 이상 이면 treeifybin 방법()안쪽 을 호출 하여 배열 이 64 이상 인지 아 닌 지 를 판단 한 다음 에 링크 를 빨 간 검 은 나무 로 바 꾸 고 빨 간 검 은 나무 에서 삽입 작업 을 수행 합 니 다.그렇지 않 으 면 링크 의 삽입 작업 을 합 니 다.옮 겨 다 니 는 과정 에서 key 가 존재 하 는 것 을 발견 하면 value 를 직접 덮어 쓰 면 됩 니 다.
4.삽입 에 성공 한 후 실제 존재 하 는 키 값 이 수량 size 에 최대 용량 threshold 를 초과 하 였 는 지 판단 하고 초과 하면 확장 합 니 다.
해시 맵 은 어떤 방법 을 사용 하여 해시 충돌 을 효과적으로 해결 하 는 지 간단하게 요약 합 니 다.
1.체인 주소 법(산 목록 사용)을 사용 하여 같은 hash 값 을 가 진 데 이 터 를 연결 합 니 다.2.2 차 교란 함수(hash 함수)를 사용 하여 해시 충돌 확률 을 낮 추고 데이터 분 포 를 더욱 고 르 게 한다.3.붉 은 검 은 나 무 를 도입 하여 옮 겨 다 니 는 시간의 복잡 도 를 더욱 낮 추어 옮 겨 다 니 는 것 이 빠르다.
그의 확장 체 제 를 살 펴 보고 먼저 소스 코드 resize()방법 을 봅 니 다.
final Node[] resize() {
Node[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node[] newTab = (Node[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode)e).split(this, newTab, j, oldCap);
else { // preserve order
Node loHead = null, loTail = null;
Node hiHead = null, hiTail = null;
Node next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
1.jdk 1.8 에서 resize 방법 은 hashmap 의 키 값 이 밸브 값(현재 용량*0.75)보다 크 거나 초기 화 될 때 resize 방법 으로 확장 하 는 것 입 니 다.
2.매번 확장 할 때마다 2 배 확장;
3.확장 후 Node 대상 의 위 치 는 원래 위치 에 있 거나 원래 의 오프셋 두 배 위치 로 이동 합 니 다.
puutVal()에서 우 리 는 이 함수 에서 2 차 resize()방법 을 사용 한 것 을 보 았 다.resize()방법 은 첫 번 째 초기 화 를 할 때 이 를 확대 하거나 이 배열 의 실제 크기 가 임계값(첫 번 째 는 12)보다 크 면 확대 하 는 동시에 수반 되 는 통 위의 요 소 를 다시 나 누 어 주 는 것 을 나타 낸다.이것 도 JDK 1.8 버 전의 최 적 화 된 부분 이다.1.7 에서 용량 을 확대 한 후에 Hash 값 을 다시 계산 하고 Hash 값 에 따라 나 누 어 주어 야 한다.그러나 1.8 버 전에 서 는 같은 통 의 위치 에서(e.hash&oldCap)가 0 인지 아 닌 지 를 판단 하고 hash 값 에 따라 분배 한 후에 이 요소 의 위 치 는 원래 위치 에 머 무 르 거나원본 위치+추 가 된 배열 크기 로 이동 하거나
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Is Eclipse IDE dying?In 2014 the Eclipse IDE is the leading development environment for Java with a market share of approximately 65%. but ac...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.