자바 에서 Array List 류 의 소스 코드 분석
17650 단어 arraylist
1:ArrayList 구성 도
2:Collection 과 List 의 차이 점
가장 좋 은 비 교 는 그들의 소스 코드 를 보 는 것 이다.우 리 는 Collection 의 모든 인 터 페 이 스 를 먼저 본다.
public interface Collection<E> extends Iterable<E> {
int size();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
}
List 인터페이스 보고 있어 요.
public interface List<E> extends Collection<E> {
int size();
boolean isEmpty();
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean addAll(int index, Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
List<E> subList(int fromIndex, int toIndex);
}
List 는 Collection 을 계승 하 는 것 이기 때문에 모든 Collection 의 모든 기능 을 가지 고 있 습 니 다.Collection 인터페이스 에서 볼 수 있 듯 이 Collection 은 색인 이 없고 요소 의 값 을 가 져 올 수 없습니다.List 는 색인 을 가 집 니 다.그러면 요 소 를 가 져 오 는 데 있어 Collection 보다 훨씬 좋 습 니 다.3:Iterable 인터페이스
Array List 에서 알 수 있 듯 이 맨 위 에 있 는 인 터 페 이 스 는 Iterable 이라는 인터페이스 입 니 다.이것 은 교체 기 입 니 다.인 터 페 이 스 는 다음 과 같 습 니 다.
public interface Iterable<T> {
Iterator<T> iterator();
}
이 인 터 페 이 스 는 주로 대상 을 되 돌려 줍 니 다.이 대상 은 Iterator 입 니 다.그러면 Iterator 인터페이스 안의 방법 을 살 펴 보 겠 습 니 다.
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
그러면 우 리 는 주로 Array List 가 어떻게 교체 기 Iterator 를 실현 하 는 지 를 본다.Iterator 의 실현 은 AbstractList 라 는 추상 적 인 클래스 의 개인 클래스 Itr 에 있 습 니 다.구체 적 으로 실현 되 는 것 을 봅 시다.
private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
cursor:색인 을 호출 할 위 치 를 기록 합 니 다.lastRet:마지막 요소 의 색인
int expectedModCount = modCount;목적 은 modCount 를 검증 하기 위해 서 입 니 다.
이 집합 에 마지막 요소 가 존재 하 는 지 판단 합 니 다.cursor 를 통 해!=size();size 는 배열 의 길 이 를 표시 합 니 다.배열 의 요소 색인 이 0 에서 시작 되 기 때문에 마지막 색인 이 배열 의 길이 와 같 을 때 배열 의 끝 에 이 르 렀 음 을 설명 합 니 다.
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
modCount:모든 배열 의 데이터 구조 변동 횟수 를 기록 합 니 다.추가,삭제,변경 등 을 포함 합 니 다.동시 다발 을 피하 기 위해 여러 스 레 드 가 동시에 작 동 할 때 특정한 스 레 드 는 배열 구 조 를 수 정 했 고 다른 스 레 드 는 이 배열 을 읽 었 습 니 다.그러면 오류 가 발생 할 수 있 습 니 다.그래서 이 코드 에 modCount 를 넣 었 습 니 다!=expected ModCount,예 를 들 어 A 스 레 드 가 데이터 구 조 를 한 번 수정 하면 modCount 는+1 이 고 expected ModCount 는 변화 가 없 기 때문에 이상 을 던 집 니 다.
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
방금 lastRet 에 기 록 된 것 이 마지막 요소 라 고 말 했 기 때문에 삭제 할 때 색인 에 따라 삭제 하면 됩 니 다.modCount 가 1 을 줄 이기 때문에 expected ModCount 를 다시 할당 하여 옮 겨 다 닐 때 오류 가 발생 하지 않도록 합 니 다.그리고 lastRed 를 차 부 초기 값 으로 합 니 다.4:분석 ArrayList
방금 목적 은 ArrayList 를 더욱 연결 하기 위해 서 였 습 니 다.ArrayList 는 우리 가 예전 에 데이터 구조 에서 언급 한 순서 표 와 마찬가지 로 Object[]배열 로 요 소 를 저장 하고 size 로 요소 의 개 수 를 기록 하 는 것 입 니 다.
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
transient 에 대해 변수 가 transient 에 의 해 수식 되면 변 수 는 더 이상 대상 의 지속 적 인 일부분 이 아 닙 니 다.그러면 왜 transient 수식 을 사용 합 니까?element Data 자체 가 캐 시 배열 이기 때문에 보통 용량 을 미리 남 겨 두 고 용량 이 부족 할 때 확장 합 니 다.예 를 들 어 현재 element Data 용량 은 10 이지 만 5 개의 요소 만 있 습 니 다.배열 의 마지막 다섯 가지 요 소 는 실제 적 인 의미 가 없고 저장 할 필요 가 없 기 때문에 Array List 의 디자이너 는 element Data 를 transient 로 설계 한 다음 에 writeObject 방법 에서 수 동 으로 직렬 화 시 키 고 전체 배열 이 아 닌 실제 저 장 된 요소 만 직렬 화 시 켰 다.writeObject 방법 을 볼 게 요.
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out array length
s.writeInt(elementData.length);
// Write out all elements in the proper order.
for (int i=0; i<size; i++)
s.writeObject(elementData[i]);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
Array List 초기 화Array List 의 디자이너 는 세 가지 방식 으로 초기 화 합 니 다.(기본 배열 용량 은 10)
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
public ArrayList() {
this(10);
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
trimToSize 방법,이 방법 은 많은 사람들 이 적 게 사용 할 수 있 습 니 다.사실은 의미 가 매우 큽 니 다.주로 쓸모없는 용량 을 제거 합 니 다.그러면 메모리 의 비용 을 줄 일 수 있 습 니 다.
public void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (size < oldCapacity) {
elementData = Arrays.copyOf(elementData, size);
}
ensureCapacity 방법 은 배열 이 가득 차 면 용량 을 늘 리 는 것 을 알 고 있 습 니 다.이 방법 은 용량 을 늘 리 는 것 입 니 다.
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
modCount 는 인 자 를 늘 리 고 배열 구 조 를 조작 하 는 횟수 를 기록 하 는 것 이다.먼저 용량 과 비교 하고 부족 하면 확대 하 는 것 이다.자바 1.6 버 전이 다.1.7>>1 을 사용 하면 모든 요소 가 오른쪽 에서 한 자 리 를 이동 한 다음 에 원래 의 용량 을 더 하 는 것 이다.그 속index Of 방법,이 방법 은 요소 색인 을 가 져 오 는 것 입 니 다.색인 을 통 해 요 소 를 조회 합 니 다.
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
이 를 통 해 알 수 있 듯 이 Array List 는 null 의 삽입 을 지원 합 니 다.똑 같이 반복 해서 찾 는 것 을 사용 하 는데 시간 이 복잡 한 것 은 n 이다.contains 방법,배열 에 어떤 요 소 를 포함 하 는 지 검증 하고 index Of 를 통 해 값 을 되 돌려 줍 니 다.
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
lastIndex Of 방법 은 index Of 와 상대 적 으로 index Of 는 이동 한 후 lastIndex Of 는 뒤에서 다음 과 같이 찾 습 니 다.
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
toArray 방법 은 List 를 배열 형식 으로 바 꾸 는 것 이다.
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
get 과 set 방법,이 건 간단 합 니 다.여러분 이 보시 면 됩 니 다.
public E get(int index) {
RangeCheck(index);
return (E) elementData[index];
}
public E set(int index, E element) {
RangeCheck(index);
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
Range Check 방법 은 검 증 된 것 입 니 다.검색 한 색인 은 배열 의 길 이 를 다음 과 같이 초과 해 서 는 안 됩 니 다.
private void RangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
}
add(E e)에 요 소 를 추가 합 니 다.이 요 소 는 꼬리 삽입 을 사용 하여 용량 을 먼저 검증 합 니 다.size+1 은 1 개의 요 소 를 추가 한 후에 길이 입 니 다.원래 배열 의 용량 이 충분 한 지 확인 합 니 다.
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add(int index,E element)는 색인 에 따라 삽입 합 니 다.첫 번 째 는 똑 같이 확장 한 다음 색인 index 뒤의 요 소 를 모두 뒤로 이동 합 니 다.System.arraycopy(elementData, index, elementData, index + 1,size - index);element Data 의 제 index 요 소 를 제 index+1 개 요소 로 옮 기 고 길 이 는 size-index 라 는 뜻 이다.
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
addAll(Collection c)
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
먼저 집합 c 를 a 배열 로 변환 한 다음 에 추가 할 배열 의 길 이 를 계산 하고 다른 기본 은 추가 요소 와 일치 합 니 다.arraycopy(Object src, int srcPos,Object dest, int destPos,int length)매개 변수 횟수 는 순서대로 소스 배열,소스 배열 의 시작 위치,목표 배열,목표 배열 의 시작 위치,배열 요소 의 수 를 복사 합 니 다.
addAll(int index, Collectionc)배열 을 지정 한 위치 에 삽입 합 니 다.
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: " + index + ", Size: " + size);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
먼저 경 계 를 넘 었 는 지 여 부 를 판단 한 다음 에 위의 기본 과 같이 확장 판단 을 한 다음 에 index 뒤의 값 을 옮 긴 다음 에 index 를 포함 한 공간 을 집합 a 에 삽입 하 는 것 이다.그래서 원 소 를 두 번 복사 합 니 다.E remove(int index)는 add 와 상대 적 으로 이 요 소 를 삭제 한 다음 index 뒤의 요 소 를 앞으로 이동 합 니 다 size-index-1 그 중에서-1 은 index 라 는 요소 가 삭제 되 고 한 요소 가 빠 지기 때 문 입 니 다.
public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
}
remove(Object o)이것 은 선진 적 인 검증 이 필요 합 니 다.그리고 이 요소 의 위 치 를 찾 아서 삭제 합 니 다.
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
}
clear 는 모든 원 소 를 비 우 는 거 예요.
public void clear() {
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
subList 방법,우 리 는 Array List 에 이런 방법 이 있다 는 것 을 알 고 있 습 니 다.Array List 소스 코드 는 존재 하지 않 습 니 다.AbstractList 를 계승 해서 왔 기 때 문 입 니 다.
public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<E>(this, fromIndex, toIndex) :
new SubList<E>(this, fromIndex, toIndex));
}
class SubList<E> extends AbstractList<E> {
private AbstractList<E> l;
private int offset;
private int size;
private int expectedModCount;
SubList(AbstractList<E> list, int fromIndex, int toIndex) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > list.size())
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
l = list;
offset = fromIndex;
size = toIndex - fromIndex;
expectedModCount = l.modCount;
}
코드 에서 우 리 는 이 기본 적 인 내부 류 의 실현 을 알 수 있다.subList 는 List 중의 한 단락 의 데이터 일 뿐이다.하지만 subList 에 대해 서 는 몇 가지 사항 을 주의해 야 합 니 다.첫째:만약 에 우리 가 List 의 수 치 를 바 꾸 면 당신 이 얻 은 subList 의 값 도 이에 따라 달라 집 니 다.이 유 는 다음 과 같 습 니 다.
public E get(int index) {
rangeCheck(index);
checkForComodification();
return l.get(index+offset);
}
이전 List 의 데 이 터 를 가 져 왔 기 때 문 입 니 다.마찬가지 로 subList 가 가 져 온 수 치 를 수정 하면 List 도 변 합 니 다.둘째:List 구 조 를 바 꾸 면 subList 를 사용 할 수 없습니다.이러한 수정 은 원래 의 list 를 바탕 으로 하기 때문에 그들 은 공동으로 list 배열 을 사용 합 니 다.
public void add(int index, E element) {
if (index<0 || index>size)
throw new IndexOutOfBoundsException();
checkForComodification();
l.add(index+offset, element);
expectedModCount = l.modCount;
size++;
modCount++;
}
5:list 삭제 오류 분석list 는 순환 삭 제 를 사용 할 때 Concurrent ModificationException 이상 을 보고 합 니 다.그러면 구체 적 인 원인 을 살 펴 보고 코드 를 먼저 보 겠 습 니 다.
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
for (String str:list){
list.remove(str);
}
foreach 가 옮 겨 다 니 기 때문에 최종 적 으로 for(Iterator it=iterator;iterators.hasNext();)모드 는 요 소 를 가 져 올 때 반드시 교체 기 에 있 는 next 방법 을 사용 합 니 다.next 방법 은 앞에서 말 했 듯 이 if(modCount!=expected ModCount)throw new Concurrent ModificationException()검증.remove(T x)방법 을 호출 할 때 modCount 가+1 이 되 기 때문에 두 번 의 비교 가 일치 하지 않 습 니 다.정확 한 표기 법 은 아래 와 같다.
Iterator iterator=list.iterator();
while (iterator.hasNext()){
iterator.next();
iterator.remove();
}
왜 교체 기 에서 remove 하면 되 는 거 죠?reove 코드 에 expected ModCount=modCount 라 는 코드 가 있 기 때 문 입 니 다.6:Array List 는 라인 이 안전 합 니까
스 레 드 가 안전 하지 않 은 것 은 여러 스 레 드 를 동시에 조작 하여 더러 운 읽 기,오독 상황 을 초래 하 는 것 을 말 합 니 다.분명 한 ArrayList 는 비 스 레 드 가 안전 합 니 다.예 를 들 어 ArrayList 는 현재 하나의 값 만 있 고 A,B2 개의 스 레 드 가 동시에 이 값 을 삭제 하면 A 스 레 드 는 size=1 을 판단 할 수 있 습 니 다.이때 시간 부분 에서 CPU 가 B 스 레 드 를 호출 하여 size 도 1 을 발견 하고 삭제 작업 을 시작 합 니 다.그리고 A 는 계속 진행 합 니 다.추가 등등.그러나 Vector 는 스 레 드 가 안전 합 니 다.그 안에 모든 방법 이 synchronized 를 추 가 했 기 때문에 모든 스 레 드 가 Array List 방법 을 실행 할 때 까지 기 다 려 야 동기 화 자 물 쇠 를 가 져 올 때 까지 계속 진행 할 수 있 습 니 다.그러면 성능 이 크게 떨 어 집 니 다.
이상 은 본 고의 모든 내용 입 니 다.본 고의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.또한 저 희 를 많이 지지 해 주시 기 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Java의 ArrayList 작동 원리 상세 정보수조로 실현하다.공간을 절약하지만 수조는 용량 제한이 있다.제한을 초과하면 50% 용량이 증가하며 System을 사용합니다.arraycopy () 를 새 그룹으로 복사합니다.따라서 수조 크기의 예비 평가를 하는 것이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.