Array
와ArrayList
는 무엇이 다르고,
ArrayList
는 어떻게 배열의 크기를 동적으로 늘리는지,grow()
메서드를 통해 알아보자
Resizable-array
라고 표현함)Vector
클래스와 거의 유사Array
는 한번 용량을 설정하면 변경 불가능ArrayList
는 용량 확장 및 축소 가능ArrayList<Integer> arrayList = new ArrayList<>(10);
arrayList.add(1); arrayList.add(2);
ArrayList
도 결국 배열로 구현되어 있지만 배열이 꽉 차면 더 큰 용량의 새로운 배열을 생성해 기존 데이터를 복사한 후 리턴해준다ArrayList
에서 그대로 capacity가 증가하는거 같지만 실제로는 copyOf()
를 통해 새로운 배열 만들어서 참조를 교체해준다...
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final int DEFAULT_CAPACITY = 10;
...
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
ArrayList
객체 생성하면 용량 0개로 설정되어 있다return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
grow()
함수 중 초기 상태에서 데이터 추가 시, 실행되는 로직이다capa = 0
)에서 (add()
함수 활용해서) 실제 데이터 추가 시, grow()
메서드를 통해 capacity가 늘어남minCapacity
)가 DEFAULT_CAPACITY
보다 작으면, 그냥 DEFAULT_CAPACITY
크기 만큼의 새로운 객체를 생성해서 returnDEFAULT_CAPACITY
보다 크면, 그 capacity만큼 새 배열 생성해준다grow()
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.**newLength**(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
private Object[] grow() {
return grow(size + 1);
}
grow(minCapacity)
는 내부적으로 ⭐ ArraysSupport.newLength(oldLength, minGrowth, prefGrowth)
⭐ 메서드를 통해 newCapacity
를 계산한다newLength()
의 매개변수oldLength
: 현재 ArrayList의 capacityminGrowth
: minCapacity - oldCapacity
로 최소로 커져야 되는 크기( 무조건 1이상)prefGrowth
: 선호되는 크기인데 기존 capacity의 절반 크기를 말함Arrays.copyOf(elementData, newCapacity)
를 활용해 newCapacity
만큼 새로운 배열을 만들고 거기에 기존 원소들을 담는다newLength()
public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
* @param oldLength current length of the array (must be nonnegative)
* @param minGrowth minimum required growth amount (must be positive)
* @param prefGrowth preferred growth amount
* @return the new array length
* @throws OutOfMemoryError if the new length would exceed Integer.MAX_VALUE
*/
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// preconditions not checked because of inlining
// assert oldLength >= 0
// assert minGrowth > 0
int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
return prefLength;
} else {
// put code cold in a separate method
return hugeLength(oldLength, minGrowth);
}
}
private static int hugeLength(int oldLength, int minGrowth) {
int minLength = oldLength + minGrowth;
if (minLength < 0) { // overflow
throw new OutOfMemoryError(
"Required array length " + oldLength + " + " + minGrowth + " is too large");
} else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
return SOFT_MAX_ARRAY_LENGTH;
} else {
return minLength;
}
}
int prefLength = oldLength + Math.max(minGrowth, prefGrowth);
minGrowth
값과 prefGrowth
값 중 큰 값을 기존 Capacity(oldLength
)에 더해 prefLength
를 구한다prefLength
가 유효 범위에 있으면 그냥 이 값 리턴이전 배열 용량
+ 이전 배열 용량 / 2
) 크기, 즉 1.5배 크기로 배열을 확장하는 것을 볼 수 있다 hugeLength()
메서드를 통해 알아서 처리add()
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
add(E e)
grow()
메서드를 통해 배열 capacity 늘리고 추가할 때 → ⇒ 추가만 하면 되는 경우가 대부분이다 → 평균적으로 add(int index, E e)
, addAll()
remove()
, clear()
removeAll()
ArrayList
의 모든 요소(n
)와 주어진 컬렉션의 모든 요소(m
)와 비교를 수행해야 함get()
, set()
, size()
, isEmpty()
indexOf()
, lastIndexOf()
, contains()
ArrayList
도 내부적으로는 배열임1.5
배로 생성된다 ⭐⭐capacity
는 바뀌지 않고 size
만 줄어든다capacity = size
만들고 싶으면 trimToSize()
메서드 호출하면 된다size
만큼의 새로운 배열을 생성해 기존 데이터 복사 후 리턴List<> arrarList = new ArrayList<>():
initialCapacity
없이 배열 선언하면 empty Object
배열을 리턴DEFAULT_CAPACITY
길이의 배열 생성 후 리턴add(E e)
인 경우, Math.max(minGrowth, prefGrowth)
이 값에서 대부분 prefGrowth
값이 더 크다 → 그래서 기존 배열의 1.5배 용량으로 리턴된다prefGrowth
= oldLength
(기존 배열 용량) >> 1
→ 기존 배열 용량 / 2minGrowth
= 1 이다new ArrayList(1)
로 생성하는 경우 prefGrowth
가 0이다prefGrowth
가 minGrowth
보다 크다addAll(Collection<? extends E> c)
인 경우, c의 길이(minGrowth
)와 기존 배열 용량의 절반(prefGrowth
) 중 더 큰 값만큼 증가된 배열을 생성한 후 리턴SOFT_MAX_ARRAY_LENGTH
를 초과하면 hugeLength
메서드를 호출int minLength = oldLength + minGrowth;
minLength
가 음수인 경우, 즉 오버플로우가 발생한 경우 → OOM 발생minLength
가 SOFT_MAX_ARRAY_LENGTH
이하인 경우 → SOFT_MAX_ARRAY_LENGTH
리턴minLength
가 Integer.MAX_VALUE - 7
~ Integer.MAX_VALUE
사이인 경우 → 그 값(minLength
)를 리턴마지막으로 정리하자면,
ArrayList
도 결국Array
로 구현되어 있고,grow()
메서드를 통해 배열을 확장시킨다.
꽉 차서 확장할 때, 대부분의 경우1.5배씩
capacity를 늘린다.