Java 8에서 ArrayList의 기본 용량이 이제 0 인 이유는 무엇입니까?


93

내가 기억 하듯이 Java 8 이전에는 기본 용량 ArrayList이 10이었습니다.

놀랍게도 기본 (무효) 생성자에 대한 주석은 여전히 ​​다음과 같이 말합니다. Constructs an empty list with an initial capacity of ten.

에서 ArrayList.java:

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

...

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

답변:


105

기술적 10으로는 백업 어레이의 지연 초기화를 인정하면 0이 아니라. 보다:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

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

    ensureExplicitCapacity(minCapacity);
}

어디

/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;

당신이 말하는 것은 처음에 비어있는 모든 ArrayList객체 사이에서 공유되는 0 크기의 초기 배열 객체입니다 . 즉, Java 7에도 존재하는 최적화 인 용량이 느리게10 보장 됩니다.

물론 생성자 계약이 완전히 정확하지는 않습니다. 아마도 이것이 혼란의 원인 일 것입니다.

배경

Mike Duigou의 이메일입니다.

빈 ArrayList 및 HashMap 패치의 업데이트 된 버전을 게시했습니다.

http://cr.openjdk.java.net/~mduigou/JDK-7143928/1/webrev/

이 개정 된 구현은 두 클래스에 새로운 필드 를 도입 하지 않습니다 . ArrayList의 경우 백업 배열의 지연 할당은 목록이 기본 크기로 생성 된 경우에만 발생합니다. 성능 분석 팀에 따르면 ArrayList 인스턴스의 약 85 %가 기본 크기로 생성되므로이 최적화는 대부분의 경우에 유효합니다.

HashMap의 경우 버킷 배열이 필요할 때까지 요청 된 초기 크기를 추적하기 위해 임계 값 필드를 창의적으로 사용합니다. 읽기 쪽에서 빈 맵 케이스는 isEmpty ()로 테스트됩니다. 쓰기 크기에서 (table == EMPTY_TABLE) 비교를 사용하여 버킷 배열을 확장해야하는 필요성을 감지합니다. readObject에는 효율적인 초기 용량을 선택하기위한 작업이 조금 더 있습니다.

출처 : http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-April/015585.html


4
에 따르면 bugs.java.com/bugdatabase/view_bug.do?bug_id=7143928 는 힙 사용 및 향상된 응답 시간을 줄이기 위해 리드 (두 appications의 숫자 것은 표시됩니다)
토마스 Kläger에게

3
@khelwood : ArrayList는이 Javadoc을 통하는 것 외에는 실제로 용량을 "보고"하지 않습니다. getCapacity()메소드가 없거나 이와 유사한 것이 없습니다. (즉, ensureCapacity(7)기본적으로 초기화 된 ArrayList의 경우 같은 작업 이 수행되지 않으므로 초기 용량이 실제로 10 인 것처럼 행동해야한다고 생각합니다...)
ruakh dec

10
좋은 파기. 기본 초기 용량은 실제로 0이 아니라 10이며 기본 케이스는 특별한 경우로 지연 할당됩니다. ArrayList인수가없는 생성자 로 생성 된 에 요소를 반복적으로 추가 하는 것과 int생성자에 0을 전달하는 경우 및 내부 배열 크기를 반사적으로 또는 디버거에서 보면이를 관찰 할 수 있습니다. 기본적으로 어레이는 길이 0에서 10으로 점프 한 다음 1.5 배 증가율에 따라 15, 22로 점프합니다. 초기 용량으로 0을 전달하면 0에서 1, 2, 3, 4, 6, 9, 13, 19로 증가합니다 ....
Stuart Marks

13
저는 변경 사항과 인용 된 이메일의 작성자 인 Mike Duigou이며이 메시지를 승인합니다. 🙂 Stuart가 말했듯이 동기는 주로 성능보다는 공간 절약에 관한 것이지만 백업 어레이 생성을 자주 피함으로써 약간의 성능 이점도 있습니다.
Mike Duigou 2015

4
@assylias :; ^) 아니요, 싱글 톤이 emptyList()여전히 여러 개의 빈 ArrayList인스턴스 보다 적은 메모리를 소비 하기 때문에 여전히 그 자리가 있습니다. 지금은 덜 중요하므로 모든 장소 에서 필요하지 않으며 , 특히 나중에 요소를 추가 할 가능성이 높은 장소에서는 필요 하지 않습니다. 또한 때때로 것을 명심 원하는 불변의 빈 목록을 다음 emptyList()길을 가야하는 것입니다.
Holger

23

Java 8에서 ArrayList의 기본 용량은 ArrayList 객체에 하나 이상의 객체를 추가 할 때까지 0입니다 (지연 초기화라고 부를 수 있음).

이제 JAVA 8에서 이러한 변경이 수행 된 이유는 무엇입니까?

대답은 메모리 소비를 줄이는 것입니다. 수백만 개의 배열 목록 객체가 실시간 Java 애플리케이션에서 생성됩니다. 10 개 객체의 기본 크기는 생성시 기본 배열에 대해 10 개의 포인터 (40 또는 80 바이트)를 할당하고이를 null로 채움을 의미합니다. 빈 배열 (null로 채워짐)은 많은 메모리를 차지합니다.

지연 초기화는 실제로 배열 목록을 사용할 때까지이 메모리 소비를 연기합니다.

도움이 필요하면 아래 코드를 참조하십시오.

ArrayList al = new ArrayList();          //Size:  0, Capacity:  0
ArrayList al = new ArrayList(5);         //Size:  0, Capacity:  5
ArrayList al = new ArrayList(new ArrayList(5)); //Size:  0, Capacity:  0
al.add( "shailesh" );                    //Size:  1, Capacity: 10

public static void main( String[] args )
        throws Exception
    {
        ArrayList al = new ArrayList();
        getCapacity( al );
        al.add( "shailesh" );
        getCapacity( al );
    }

    static void getCapacity( ArrayList<?> l )
        throws Exception
    {
        Field dataField = ArrayList.class.getDeclaredField( "elementData" );
        dataField.setAccessible( true );
        System.out.format( "Size: %2d, Capacity: %2d%n", l.size(), ( (Object[]) dataField.get( l ) ).length );
}

Response: - 
Size:  0, Capacity:  0
Size:  1, Capacity: 10

기사 Java 8에서 ArrayList의 기본 용량에 대해 자세히 설명합니다.


7

ArrayList로 수행되는 첫 번째 작업 addAll이 10 개 이상의 요소가있는 컬렉션 을 전달 하는 것이라면 ArrayList의 내용을 보관하기 위해 초기 10 개 요소 배열을 만드는 데 드는 노력은 창 밖으로 던져집니다. ArrayList에 무언가가 추가 될 때마다 결과 목록의 크기가 백업 저장소의 크기를 초과하는지 여부를 테스트해야합니다. 초기 백업 저장소의 크기가 10이 아닌 0이되도록 허용하면 첫 번째 작업이 초기 10 개 항목 배열을 만들어야하는 "추가"인 목록의 수명 동안이 테스트가 한 번 더 실패하지만 비용은 다음과 같습니다. 사용되지 않는 10 개 항목 배열을 만드는 비용보다 적습니다.

즉, 현재 항목 이후에 목록에 추가 될 항목 수 (있는 경우)를 지정하는 "addAll"의 오버로드가있는 경우 일부 컨텍스트에서 성능을 더욱 향상시킬 수 있었을 수 있습니다. 이를 사용하여 할당 동작에 영향을줍니다. 경우에 따라 목록에 마지막 몇 개의 항목을 추가하는 코드는 목록에 그 이상의 공간이 필요하지 않다는 매우 좋은 생각을 가질 것입니다. 목록이 한 번 채워지고 그 후에는 수정되지 않는 상황이 많이 있습니다. 포인트 코드에서 목록의 최종 크기가 170 ​​개 요소라는 것을 알고 있다면 150 개 요소와 160 개 크기의 백업 저장소가 있습니다.


에 대한 아주 좋은 점 addAll(). 이는 첫 번째 malloc을 중심으로 효율성을 개선 할 수있는 또 다른 기회입니다.
kevinarpe

@kevinarpe : Java의 라이브러리가 프로그램이 어떻게 사용되는지를 나타내는 더 많은 방법으로 설계 되었으면합니다. 예를 들어 이전 스타일의 하위 문자열은 일부 사용 사례에서는 형편 없지만 다른 경우에는 훌륭했습니다. "원본보다 오래 지속될 가능성이있는 부분 문자열"과 "원본보다 오래 지속될 것 같지 않은 부분 문자열"에 대한 별도의 기능이 있고 코드가 90 %의 시간 동안 올바른 부분을 사용했다면, 두 기능 중 어느 쪽이든 크게 능가 할 수 있었다고 생각합니다. 이전 또는 새 문자열 구현.
supercat

3

질문은 '왜?'입니다.

메모리 프로파일 링 검사 (예 : https://www.yourkit.com/docs/java/help/inspections_mem.jsp#sparse_arrays )는 빈 (null로 채워진) 배열이 많은 메모리를 차지함을 보여줍니다.

10 개 객체의 기본 크기는 생성시 기본 배열에 대해 10 개의 포인터 (40 또는 80 바이트)를 할당하고이를 null로 채움을 의미합니다. 실제 자바 애플리케이션은 수백만 개의 배열 목록을 생성합니다.

도입 된 수정 사항은 ^ W를 제거하여 실제로 배열 목록을 사용할 때 까지이 메모리 소비를 연기합니다.


"쓰레기"로 "소비"를 수정하십시오. 당신이 제공하는 링크는 그들이 모든 곳에서 메모리를 고갈시키기 시작한다는 것을 의미하지 않으며, 단지 null 요소가있는 배열이 그들에게 할당 된 메모리를 불균형하게 낭비한다는 것을 의미합니다. "소비"는 할당을 넘어서 메모리를 마술처럼 사용한다는 것을 의미합니다.
mechalynx

0

JAVA 8의 ArrayList 기본 크기는 stil 10입니다. JAVA 8의 유일한 변경 사항은 코더가 10 개 미만의 요소를 추가하면 나머지 arraylist 공백 위치가 null로 지정되지 않는다는 것입니다. 내가 이런 상황과 일식을 겪었 기 때문에 그렇게 말하면 JAVA 8의 변경 사항을 살펴볼 수 있습니다.

아래 스크린 샷을보고이 변경을 정당화 할 수 있습니다. Object [10]에서 ArrayList 크기가 10으로 지정되어 있지만 표시되는 요소의 수는 7 개에 불과하다는 것을 알 수 있습니다. 나머지 null 값 요소는 여기에 표시되지 않습니다. JAVA 7에서 스크린 샷은 JAVA 8에서이 부담이 제거되는 동안 전체 배열 목록을 반복하는 경우 코더가 null 값을 처리하기위한 코드를 작성해야하는 null 값 요소도 표시되는 단일 변경 사항과 동일합니다. 코더 / 개발자의 책임자.

스크린 샷 링크.


0

위의 질문 후에 Java 8의 ArrayList Document를 살펴 보았습니다. 기본 크기는 여전히 10입니다.

아래를 참조하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.