소스 코드 분석 --ArrayList

머리말

이 문서에서 우리는 List 인터페이스를 구현하는 또 다른 컨테이너 클래스, ArrayList를를 분석, 구현 클래스 목록이며, 기본이되는 구현이 배열은, 우리가 아래에서 자세히 분석 할 수 있습니다. PS : 몇 시간 전에 학교 실험실 코스가 블로그를 업데이트하지 않았습니다 오랜 시간 동안 등 일 플러스 최종 시험의 모든 종류를 설정하기 때문에 ...

1. 개요

클래스 다이어그램 사이


가능해, RandomAccess ArrayList 클래스가 인터페이스를 구현, 랜덤 액세스를 지원합니다.

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
复制代码
기본 어레이 (10)의 크기,는 데이터 기억으로부터 elementData이 배열에서 볼 수있다.
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;复制代码

2. 확장

사용 ensureCapacityInternal 요소, 당신은 확장을 위해 성장 촉진 () 메소드를 사용할 필요가 충분하지 않은 경우, 충분한 용량을 보장하기 위해 () 메소드를 추가, 새로운 용량의 크기는 oldCapacity + (oldCapacity >> 1), 인 기존의 1.5 배 용량 .
당신이 복사 작업, 작업의 높은 비용을 통과하는 새로운 배열에 전체 원래 배열을 복사 Arrays.copyOf ()를 호출 할 필요가있을 때 증설 작업의 대략적인 크기를 지정하기 위해서는, ArrayList에 객체를 생성 운영 확장을 감소하는 것이 가장 좋습니다 횟수.

주목해야한다 동작의 확장은 또한 스레드 안전 동작이다. 국경 간 접속의 문제가 배열 다중 스레드 환경에서 나타나지 않도록 라인이 문은 원자 동작 아니다. 그래서 는 HashMap처럼 ArrayList의 스레드 안전 컨테이너입니다 .

elementData[size++] = e;复制代码

다음은 새로운 요소는 소스 코드 및 확장 작업에 삽입한다 :

//插入新元素
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //这里会产生数组越界问题,因为下面这行语句并不是一个原子操作
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
//扩容
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //新容量为旧容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}复制代码

3. 분리 소자

+ 1 개 요소는 인덱스 위치에 복사됩니다 인덱스 뒤에 ()를 호출 할 필요가 System.arraycopy에 운영 시간 복잡도는 O (N)이며 , 비용이 ArrayList의 요소를 삭제 볼 수는 매우 높다.

public E remove(int index) {
    rangeCheck(index);
    modCount++;
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // clear to let GC do its work
    return oldValue;
}复制代码

4. 르파

약 4.1 르파

르파 하나의 오류 메커니즘의 메커니즘 자바 컬렉션 (컬렉션). 다중 스레드는 내용의 동일한 세트에서 작동 할 때, 르파 이벤트를 생성 할 수 있습니다.
예를 들어 스레드 반복기는 수집 과정을 통해 통과 할 때, 세트는 다른 스레드에 의해 변경된 경우, 콘텐츠 후 예외의 ConcurrentModificationException이 르파 이벤트를 생성 발생한다하는 컬렉션에 액세스 쓰레드.

르파 4.2의 ArrayList에서

ArrayList에의 modCount는 구조의 변화의 수를 기록하는 데 사용 . 작동, 내부 배열 구조에서 모든 변경의 크기를 조정 부가 적어도 하나 개의 요소를 삭제하는 요소의 값을 그냥 구조의 변경 설정을 의미한다.

ArrayList와 나 등 반복의 순서를 수행 할 때, 당신은 전에 변경 여부 동작의 modCount 후 비교해야 변화가 ConcurrentModificationException를 올릴 수 있다면.

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 size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // 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();
    }
}复制代码

시퀀스

배열 ArrayList를 구현하고 동적 특성의 팽창 계 때문에 반드시 저장되지 않은 배열 요소가 사용되는 모든 직렬화가 필요 없다.
배열이 직렬화되지 않습니다 선언의 기본 키워드의 일시적인 변경을 사용으로부터 elementData 배열의 요소를 저장합니다.

transient Object[] elementData; // non-private to simplify nested class access复制代码
ArrayList를 배열 순서 만 원소 함량의 부분을 채우도록 갖고 제어의 writeObject ()와 readObject에 ()를 구현한다.

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}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 size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // 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();
    }
}复制代码
당신은 변환 할 직렬화의 writeObject ObjectOutputStream에 () 객체 및 출력 바이트 스트림을 사용해야합니다. 이 물체의 반사로 이동합니다 때의 writeObject () 메소드는 수신 객체의 writeObject ()를 가지고하는 것은 () 직렬화를 달성하기 위해 writeObject에 호출합니다. ObjectInputStream를 readObject에 ()에있어서, 직렬화 및 역 직렬화 유사한 원리를 사용하여 역 직렬화.

ArrayList list = new ArrayList();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(list);复制代码

6. 요약

뷰의 소스 포인트에서의 ArrayList 제거) 일부 용지는 추가 (같은 몇몇 일반적으로 사용되는 방법으로, 내부 데이터 구조의 ArrayList를 분석 HashMap의보다 다소 더 간단 할 수있다 (). 또한 다중 스레드 환경이 스레드 안전 컨테이너가 다중 스레드 환경에서 사용하려는 경우, 또한 ArrayList에있는 르파 소개, 벡터으로 CopyOnWriteArrayList 또는 두 개의 컨테이너를 사용하는 것이 좋습니다 이유 논의 응용 프로그램과 ArrayList를 직렬화 문제.

마지막으로,이 문서의 단점을 지적, 서로 논의하기 위해 당신을 환영합니다.


추천

출처juejin.im/post/5d29d66de51d45108c59a5f0