Java 집합 상세 설명 - Array List

개술
ArrayList 는 AbstractList 에서 계승 하여 List 인 터 페 이 스 를 실현 하 였 으 며, 본질 적 으로 기본 초기 길이 가 10 이 며, 자동 확장 을 지원 하 는 배열 입 니 다.ArrayList 비 스 레 드 안전, 다 중 스 레 드 환경 에서 작업 하려 면 개발 자가 스 레 드 안전 을 확보 해 야 합 니 다.
관건 변수 와 상수
    //       
    private static final int DEFAULT_CAPACITY = 10; 

    //     ,    ArrayList  . 
    transient Object[] elementData; 

초기 화
Array List 는 세 가지 초기 화 방법 을 제공 합 니 다.
  • 부전 삼
  •     /**
         * Constructs an empty list with an initial capacity of ten.
         */
        public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }

    이 방식 으로 ArrayList 를 만 들 때 elementData 빈 배열 로 초기 화 하고 DEFAULT_CAPACITY = 10 초기 용량 으로 사용 합 니 다.
  • 초기 용량
  •     /**
         * Constructs an empty list with the specified initial capacity.
         *
         * @param  initialCapacity  the initial capacity of the list
         * @throws IllegalArgumentException if the specified initial capacity
         *         is negative
         */
        public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }

    초기 화 시 초기 용량 을 입력 하고 elementData 길 이 를 대응 하 는 배열 로 초기 화 합 니 다. 0 에 들 어 갈 때 첫 번 째 초기 화 방법 과 같 고 0 보다 작은 용량 으로 들 어 갈 때 IllegalArgumentException 이상 을 던 집 니 다.
  • 집합 전송
  •     /**
         * Constructs a list containing the elements of the specified
         * collection, in the order they are returned by the collection's
         * iterator.
         *
         * @param c the collection whose elements are to be placed into this list
         * @throws NullPointerException if the specified collection is null
         */
        public ArrayList(Collection extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }

    이 방법 은 들 어 오 는 집합 을 배열 로 바 꾸 고 elementData 에 값 을 부여 하 는 것 을 잘 이해 할 수 있다. 관건 은 if (elementData.getClass() != Object[].class) 이 줄 코드 에 이 판단 을 더 한 의 도 는 무엇 입 니까?주석 의 의 미 는 c.toArray() 되 돌아 오 는 것 이 꼭 Object [] 유형 이 아니 라 는 것 입 니 다. 여기 서 알 아 봐 야 합 니 다 JDK-6260652 Bug
    예 를 들 어 다음 코드 를 사용 하여 Array List 를 만 듭 니 다.
            List l1 = Arrays.asList(new String[]{"123"});
            Object[] objs = l1.toArray();
            System.out.println(objs.getClass()); //     class [Ljava.lang.String;
    Arrays.asList(new String[]{"123"}).toArray().getClass() 유형 은 String[] 유형 이 아 닌 Object[] 유형 으로 되 돌아 가 고, 재 집행 objs[0] = new Object(); 하면 java.lang.ArrayStoreException 이상 을 던진다.
    구체 적 인 원인 은 Arrays#ArrayList.toArray() 방법 을 보아 야 한다.
            public  T[] toArray(T[] a) {
                int size = size();
                if (a.length < size)
                    return Arrays.copyOf(this.a, size,
                                         (Class extends T[]>) a.getClass());
                System.arraycopy(this.a, 0, a, 0, size);
                if (a.length > size)
                    a[size] = null;
                return a;
            }

    상기 코드 에서 보 듯 이 Arrays#ArrayList.toArray()T[] 유형 을 되 돌려 주 고 String 배열 에 들 어 오 면 되 돌아 오 는 배열 유형 은 String[] 유형 이다.하나의 String[] 유형 배열 에 대해 하나의 Object 값 을 부여 하면 오류 가 발생 할 수 있 습 니 다.
    따라서 위의 판단 이 있 고 Object[] 유형 인지 판단 하 며 그렇지 않 으 면 배열 을 Object[] 유형 으로 전환 하여 후속 할당 시 java.lang.ArrayStoreException 이상 이 발생 하지 않도록 한다.
    획득
        public E get(int index) {
            rangeCheck(index);
            return elementData(index);
        }
    
        private void rangeCheck(int index) {
            if (index >= size)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    ArrayList 아래 표 시 를 통 해 요 소 를 얻 을 때 먼저 index 경 계 를 넘 었 는 지 여 부 를 판단 하고 경 계 를 넘 으 면 IndexOutOfBoundsException 이상 을 던 집 니 다. 그렇지 않 으 면 아래 표 시 된 요 소 를 되 돌려 줍 니 다.
    덧붙이다
    Array List 에 요 소 를 추가 하 는 방법 은 두 가지 가 있 습 니 다.
  • 지 정 된 위치 에 요 소 를 추가 합 니 다
  •     public void add(int index, E element) {
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }
        
        //   index    
        private void rangeCheckForAdd(int index) {
            if (index > size || index < 0)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
        
        private void ensureCapacityInternal(int minCapacity) {
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
    
        //          ,  elementData    ,  DEFAULT_CAPACITY, minCapacity      
        //        minCapacity
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            return minCapacity;
        }
    
        //           ,   ,  grow    
        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;
            //     ,        50%
            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);
        }

    먼저 index 경 계 를 넘 었 는 지, 경 계 를 넘 어 IndexOutOfBoundsException 이상 을 던 진 다음 에 배열 의 확장 이 필요 한 지 판단 합 니 다. 확장 이 필요 하 다 면 확장 후 요 소 를 지정 index 에 추가 합 니 다. 구체 적 으로 코드 설명 을 보십시오.
  • 끝 에 원 소 를 추가 합 니 다
  •     public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }

    주요 절 차 는 위의 방법 과 유사 하여 붙 이지 않 는 다.
    삭제
  • 지 정 된 위치의 요소 삭제
  •     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;
        }

    일반적인 조작 은 첫 번 째 단계 에서 먼저 경 계 를 넘 었 는 지 여 부 를 판단 한다.이 곳 modCount 변 수 는 구조 변화 가 발생 한 횟수 를 통계 하 는 데 사 용 됩 니 다. ArrayList 의 값 이 예상 과 맞지 않 으 면 modCount 이상 을 던 집 니 다. 보통 옮 겨 다 닐 때 ConcurrentModificationException 요 소 를 삭제 하거나 추가 하 는 장면 이 나타 납 니 다.그 다음 에 대응 하 는 ArrayList 의 값 을 얻 고 이동 할 요소 의 수량 을 계산 합 니 다. index 이 있 으 면 numMoved > 0 을 통 해 System.arraycopy 다음 index + 1 요 소 를 numMoved 위치 로 복사 합 니 다.만약 index, 직접 할당 numMoved == 0, GC 회수 대기
  • 첫 번 째 로 찾 은 대상 삭제
  •     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; // clear to let GC do its work
        }    

    이 방법 은 대상 에 전송 되 어 null 에 대응 하 는 첫 번 째 대상 을 찾 은 후 삭제 합 니 다. 절차 와 위의 방법 은 차이 가 많 지 않 습 니 다.
    바꾸다
    지정 한 위치의 요소 바 꾸 기
        public E set(int index, E element) {
            rangeCheck(index);
    
            E oldValue = elementData(index);
            elementData[index] = element;
            return oldValue;
        }

    먼저 ArrayList 선 을 넘 었 는 지 여 부 를 판단 하고 선 을 넘 어 index 이상 을 던 졌 으 며, 선 을 넘 지 않 으 면 IndexOutOfBoundsException 에서 대응 elementData 의 값 을 얻 고 대응 index 의 값 을 새 값 으로 설정 한 후 되 돌려 indexmodCount
    배열 구조 변경 횟수 를 기록 하 는 데 사용 되 며, 매번 요 소 를 추가 하거나 삭제 할 때마다 + 1. 교체 기 를 사용 할 때 oldValue 예상 과 일치 하지 않 으 면 modCount 이상 을 던 집 니 다.

    좋은 웹페이지 즐겨찾기