Java 컬렉션 프레임워크에서의 교체기 Iterator 해석
본고는 주로 자바 집합 프레임워크의 교체기 부분, Iterator를 분석하고자 합니다. 이 원본 분석은 JDK1.8, 분석 도구, AndroidStudio를 바탕으로 합니다. 문장 분석이 부족한 점을 지적하십시오!
1. 소개
우리는 JDK가 제공하는 교체 인터페이스를 자주 사용하여 Java 집합의 교체를 진행한다.
Iterator iterator = list.iterator();
while(iterator.hasNext()){
String string = iterator.next();
//do something
}
위쪽은 바로 교체기가 사용하는 기본 템플릿이다. 교체는 사실 우리는 간단하게 반복이라고 이해할 수 있으며 각종 용기 안의 모든 대상을 표준화하는 방법류이다.이것은 항상 Iterator를 제어해서'앞으로','뒤로','현재 요소를 가져오라'는 명령을 보내면 전체 집합을 간접적으로 훑어볼 수 있습니다.Java에서 Iterator는 하나의 인터페이스로 기본 규칙만 교체합니다.
public interface Iterator<E> {
//
boolean hasNext();
// , E
E next();
//
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
위쪽은 바로 교체기의 기본적인 설명이고 우리는 구체적인 집합을 통해 분석한다.2. 집합 분류
2.1 ArrayList의 Iterator
우리는 ArrayList의 원본 코드를 분석함으로써 알 수 있듯이 ArrayList 내부에서 먼저 내부 클래스 Itr를 정의하고 이 내부 클래스는 Iterator 인터페이스를 실현한다. 다음과 같다.
private class Itr implements Iterator<E> {
//....
}
내부 클래스는 Iterator 인터페이스를 실현했고 Array List의 Iterator는 내부 클래스인 Itr를 되돌려주었다. 그래서 우리는 주로 Itr가 어떻게 실현되었는지 보았다.
public Iterator<E> iterator() {
return new Itr();
}
이어서 우리는 그것의 내부 유형인 Itr의 실현 방식을 분석한다.
private class Itr implements Iterator<E> {
protected int limit = ArrayList.this.size;
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor < limit;
}
@SuppressWarnings("unchecked")
public E next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
int i = cursor;
if (i >= limit)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
limit--;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
먼저 정의된 변수를 분석합니다.
protected int limit = ArrayList.this.size;
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
그 중에서limit는 현재arrayList의 크기이고cursor는 다음 요소의 인덱스를 대표하며lastRet는 이전 요소의 인덱스입니다. 없으면 -1을 되돌려줍니다. expectedModCount는 별로 쓸모가 없습니다.우리는 이어서 교체할 때 후계 원소가 있는지 없는지를 어떻게 판단할 것인가를 분석했다.
public boolean hasNext() {
return cursor < limit;
}
간단하다. 바로 다음 원소의 인덱스가 수조의 용량 크기에 도달했는지 판단하는 것이다. 도달하면 없다. 끝이다!이어서 현재 인덱스 요소를 가져오는 방법인next
public E next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
int i = cursor;
if (i >= limit)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
넥스트 방법에서 왜 modCount를 판단합니까?반복 과정에서 집합이 수정되었는지 판단하는 데 쓰인다.modCount는 ArrayList 집합의 수정 횟수를 기록하는 데 사용되며 0으로 초기화되고 집합이 수정될 때마다 (구조상의 수정, 내부 업데이트는 포함되지 않습니다.) 예를 들어add,remove 등 방법,modCount+1이기 때문에modCount가 변하지 않으면 집합 내용이 수정되지 않았음을 나타냅니다.이 메커니즘은 주로 ArrayList 집합을 실현하는 데 사용되는 빠른 실패 메커니즘으로 자바의 집합에서 비교적 큰 부분의 집합은 빠른 실패 메커니즘이 존재한다.그러므로 반복 과정에서 오류가 발생하지 않도록 하려면 반복 과정에서 집합에 구조적인 수정이 일어나지 않도록 해야 한다. (물론remove 방법 제외) 이상 오류가 발생하면catch 이후에 처리하지 않는 것이 아니라 프로그램이 오류가 발생했는지 진지하게 검사해야 한다.위의 코드는 비교적 간단하다. 바로 색인에 있는 그룹 값을 되돌려주는 것이다.ArrayList의 교체 방법은 주로 인덱스의 값과 그룹의 크기를 판단하여 비교하는 것이다. 아직 데이터가 돌아다니지 않은 것을 보고 차례대로 그룹의 값을 얻는다. 단, 각 집합의 밑바닥 실현 방식을 잡으면 교체할 수 있다.
다음은 HashMap의 Iterator 방법을 분석해 보겠습니다. 다른 방법은 유사합니다. 밑바닥 실현 방식만 잡으면 됩니다.
2.2 HashMap의 Iterator
Hash Map에서도 하나의 클래스가 Iterator 인터페이스를 실현했다. 단지 추상적인 클래스일 뿐이다. Hash Iterator. 그 실현 방식을 살펴보자.
private abstract class HashIterator<E> implements Iterator<E> {
HashMapEntry<K,V> next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
HashMapEntry<K,V> current; // current entry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
HashMapEntry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
마찬가지로 그것도 변수를 정의했다
HashMapEntry<K,V> next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
HashMapEntry<K,V> current; // current entry
다음 entry의 노드를 대표합니다.expectedModCount 역시 수정 상태를 판단하고 집합의 빠른 실패 메커니즘에 사용됩니다.index는 현재 인덱스를 대표하고,current는 현재 인덱스가 대표하는 노드entry입니다. 다음 요소의 값이 있는지 어떻게 판단하는지 봅시다.
public final boolean hasNext() {
return next != null;
}
간단하게next가null인지 아닌지를 판단하는 것이다. null이면 데이터가 없다는 뜻이다.이어서 원소를 얻는 방법을 분석한다
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
HashMapEntry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
// Entry
// Entry , next ;
// , next ( Entry) null 。
if ((next = e.next) == null) {
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
이상은 바로 일부 구체적인 집합 실례의 교체 방법의 실현 원리이고 같은 이치로 다른 집합의 실현 방식을 분석할 수 있다.이상은 본문의 전체 내용입니다. 여러분의 학습에 도움이 되고 저희를 많이 응원해 주십시오.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.