[java] 초기 용량으로 ArrayList를 시작하는 이유는 무엇입니까?

일반적인 생성자 ArrayList는 다음과 같습니다.

ArrayList<?> list = new ArrayList<>();

그러나 초기 용량에 대한 매개 변수가있는 오버로드 된 생성자가 있습니다.

ArrayList<?> list = new ArrayList<>(20);

원하는 ArrayList대로 추가 할 수있을 때 초기 용량 으로 생성하는 것이 유용한 이유는 무엇 입니까?



답변

크기를 미리 알고 있다면 ArrayList초기 용량을 지정하는 것이 더 효율적입니다. 이 작업을 수행하지 않으면 목록이 커짐에 따라 내부 배열을 반복적으로 재 할당해야합니다.

최종 목록이 클수록 재 할당을 피함으로써 더 많은 시간을 절약 할 수 있습니다.

즉, 사전 할당이 없어도 n뒷면에 요소를 삽입 ArrayList하는 데 총 O(n)시간이 걸립니다. 다시 말해, 요소를 추가하는 것은 상각 된 상수 시간 연산입니다. 이것은 각각의 재 할당이 어레이의 크기를 지수 적으로, 전형적으로 씩 증가시킴으로써 달성된다 1.5. 이 방법을 사용하면 총 작업 수를로 표시 할 수 있습니다O(n) .


답변

왜냐하면 ArrayListA는 동적 리사이징 어레이 는 초기 (기본) 고정 크기 어레이로서 구현되는 수단, 데이터 구조. 이것이 채워지면 배열은 두 배 크기로 확장됩니다. 이 작업은 비용이 많이 들기 때문에 최대한 적게 원합니다.

따라서 상한이 20 개의 항목 인 경우 초기 길이가 20 인 배열을 만드는 것이 기본값 인 15를 사용하는 것보다 낫습니다. 그런 다음 15*2 = 30확장주기를 낭비하면서 크기를 조정하고 20 만 사용하십시오.

PS-AmitG가 말했듯이 확장 요소는 구현에 따라 다릅니다 (이 경우 (oldCapacity * 3)/2 + 1)


답변

Arraylist의 기본 크기는 10 입니다.

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

따라서 100 개 이상의 레코드를 추가하려는 경우 메모리 재 할당 오버 헤드를 볼 수 있습니다.

ArrayList<?> list = new ArrayList<>();
// same as  new ArrayList<>(10);      

따라서 Arraylist에 저장 될 요소 수에 대한 아이디어가 있다면 10으로 시작한 다음 증가시키는 대신 해당 크기의 Arraylist를 만드는 것이 좋습니다.


답변

나는 실제로 2 개월 전에 주제에 대한 블로그 게시물 을 썼습니다 . 이 기사는 C #에 대한 List<T>것이지만 Java의 ArrayList구현은 매우 유사합니다. ArrayList동적 배열을 사용하여 구현 되므로 필요에 따라 크기가 커집니다. 용량 생성자의 이유는 최적화를위한 것입니다.

이러한 크기 조정 작업 중 하나가 발생하면 ArrayList는 배열의 내용을 기존 배열의 용량의 두 배인 새 배열로 복사합니다. 이 작업은 O (n) 시간에 실행됩니다 .

다음은 ArrayList크기가 어떻게 증가 하는지에 대한 예입니다 .

10
16
25
38
58
... 17 resizes ...
198578
297868
446803
670205
1005308

따라서 목록의 용량은 1011 번째 항목이 추가 될 때로 증가 50% + 1합니다 16. 17 번째 항목에서 ArrayList가 다시 증가합니다 25. 이제 원하는 용량이 이미 알려진 목록을 작성하는 예를 고려하십시오 1000000. ArrayList크기 생성자를 사용하지 않고 생성하면 크기 조정시 O (1) 또는 O (n)ArrayList.add 1000000 이 걸리는 시간 이 호출됩니다 .

1000000 + 16 + 25 + … + 670205 + 1005308 = 4015851 연산

생성자를 사용하여 이것을 비교 한 다음 O (1)ArrayList.add 에서 실행되도록 보장하는 호출하십시오 .

1000000 + 1000000 = 2000000 연산

자바 대 C #

Java는 위와 같으며에서 시작하여 10각 크기를 조정 50% + 1합니다. C #은 시작될 4때마다 훨씬 더 적극적으로 증가하여 크기를 조정할 때마다 두 배가됩니다. 1000000C # 사용 3097084작업에 대한 위 의 추가 예제 .

참고 문헌


답변

예를 들어 ArrayList의 초기 크기를 설정하면 ArrayList<>(100)내부 메모리의 재 할당 횟수가 줄어 듭니다.

예:

ArrayList example = new ArrayList<Integer>(3);
example.add(1); // size() == 1
example.add(2); // size() == 2, 
example.add(2); // size() == 3, example has been 'filled'
example.add(3); // size() == 4, example has been 'expanded' so that the fourth element can be added. 

위의 예에서 볼 수 있듯이 ArrayList필요한 경우 확장 할 수 있습니다. 이것이 표시하지 않는 것은 Arraylist의 크기가 일반적으로 두 배라는 것입니다 (새 크기는 구현에 따라 다릅니다). 다음은 Oracle 에서 인용 한 것입니다 .

“각 ArrayList 인스턴스에는 용량이 있습니다. 용량은 목록에 요소를 저장하는 데 사용되는 배열의 크기입니다. 항상 최소한 목록 크기보다 큽니다. 요소가 ArrayList에 추가되면 용량이 자동으로 증가합니다. 성장 정책에 대한 세부 사항은 요소를 추가하는 것이 상각 된 상각 시간 비용이라는 사실을 넘어서 명시되어 있지 않다. “

분명히, 어떤 종류의 범위를 보유할지 모를 경우 크기를 설정하는 것은 좋은 생각이 아닙니다. 그러나 특정 범위를 염두에두고 초기 용량을 설정하면 메모리 효율성이 향상됩니다. .


답변

ArrayList는 많은 값을 포함 할 수 있으며 큰 초기 삽입을 수행 할 때 다음 항목에 더 많은 공간을 할당하려고 할 때 CPU주기를 낭비하지 않도록 시작하여 더 큰 스토리지를 할당하도록 ArrayList에 지시 할 수 있습니다. 따라서 처음에 약간의 공간을 할당하는 것이 더 효과적입니다.


답변

이것은 모든 단일 객체에 대한 재 할당 노력을 피하기위한 것입니다.

int newCapacity = (oldCapacity * 3)/2 + 1;

내부적 new Object[]으로 생성됩니다. arraylist에 요소를 추가 할 때
JVM을 작성해야합니다 new Object[]. 당신이 호출 할 때 다음 재 할당을위한 코드 (당신이 너 한테 어떤 생각) 위의 모든 시간이 없다면 arraylist.add()다음 new Object[]무의미하다 만들 수있다 우리는 추가 할 각각의 모든 객체에 대해 1 씩 크기를 증가시키기위한 시간을 잃어버린 있습니다. 따라서 Object[]다음 수식으로 크기를 늘리는 것이 좋습니다 .
(JSL은 매번 1 씩 증가하는 대신 동적으로 배열 목록을 증가시키기 위해 아래에 주어진 캐스팅 공식을 사용했습니다. 성장하기 위해서는 JVM이 노력해야합니다)

int newCapacity = (oldCapacity * 3)/2 + 1;