Java의 ArrayList
소스 코드를 읽고 if 문에서 몇 가지 비교를 발견했습니다.
Java 7에서이 방법 grow(int)
은
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
Java 6 grow
에는 존재하지 않았습니다. ensureCapacity(int)
그러나이 방법 은
if (newCapacity < minCapacity)
newCapacity = minCapacity;
변화의 원인은 무엇입니까? 성능 문제입니까 아니면 스타일입니까?
나는 0과 비교하는 것이 더 빠르다고 생각할 수 있지만, 음수인지 확인하기 위해 완전한 빼기를 수행하는 것은 나에게 약간 과잉 것 같습니다. 또한 바이트 코드와 관련 하여 하나 ( ) 대신 두 개의 명령어 ( ISUB
및 IF_ICMPGE
) 가 필요 IFGE
합니다.
답변
a < b
그리고 a - b < 0
다른 두 가지를 의미 할 수있다. 다음 코드를 고려하십시오.
int a = Integer.MAX_VALUE;
int b = Integer.MIN_VALUE;
if (a < b) {
System.out.println("a < b");
}
if (a - b < 0) {
System.out.println("a - b < 0");
}
실행하면이 인쇄 만됩니다 a - b < 0
. 일어나는 일은 a < b
분명히 거짓이지만 a - b
넘쳐서되며 -1
이는 부정적인 것입니다.
이제 말했듯이 배열의 길이가 실제로 거의 같다고 생각하십시오 Integer.MAX_VALUE
. 코드 ArrayList
는 다음과 같습니다.
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
oldCapacity
정말 가까운 Integer.MAX_VALUE
그래서 newCapacity
(인 oldCapacity + 0.5 * oldCapacity
) 오버플 될 수있다 Integer.MIN_VALUE
(즉, 음수). 그런 다음 minCapacity
언더 플로를 빼면 양수로 다시 돌아갑니다.
이 검사는가 if
실행되지 않았는지 확인합니다 . 코드는 다음과 같이 작성한다면 if (newCapacity < minCapacity)
, 그것은 것 true
(이후이 경우 newCapacity
이 때문에 음수) newCapacity
로 강제 될 수 minCapacity
에 관계없이 중 oldCapacity
.
이 오버플로 사례는 다음 if에 의해 처리됩니다. 때 newCapacity
용량이 초과되었습니다,이 될 것이다 true
: MAX_ARRAY_SIZE
로 정의 Integer.MAX_VALUE - 8
하고 Integer.MIN_VALUE - (Integer.MAX_VALUE - 8) > 0
있다 true
. 는 newCapacity
따라서 바르게 처리됩니다 hugeCapacity
방법 반환 MAX_ARRAY_SIZE
또는 Integer.MAX_VALUE
.
NB : 이것이이 // overflow-conscious code
방법 의 의견입니다.
답변
이 설명을 찾았습니다 .
2010 년 3 월 9 일 화요일 03:02에 Kevin L. Stern은 다음과 같이 썼습니다.
빠른 검색을했는데 Java가 실제로 2의 보수 기반 인 것으로 보입니다. 그럼에도 불구하고, 일반적으로 이러한 유형의 코드는 어떤 시점에서 누군가가 Dmytro가 제안한 것을 따라 와서 정확하게 수행 할 것으로 기대하기 때문에 일반적 으로이 유형의 코드가 나를 걱정한다는 것을 지적하십시오. 즉, 누군가가 바뀔 것입니다.
if (a - b > 0)
에
if (a > b)
배 전체가 가라 앉습니다 나는 개인적으로 정수 오버플로를 알고리즘의 필수 기반으로 만드는 것과 같은 모호한 것을 피하는 것이 좋습니다. 일반적으로 오버플로를 피하고 오버플로 시나리오를보다 명시 적으로 만드는 것을 선호합니다.
if (oldCapacity > RESIZE_OVERFLOW_THRESHOLD) { // Do something } else { // Do something else }
좋은 지적입니다.
에
ArrayList
있기 때문에 우리는 (또는 적어도하지 양립)이 작업을 수행 할 수있는
ensureCapacity
공개 API이고 효과적으로 이미 충족 할 수없는 양의 용량에 대한 요청으로 음수를 받아들입니다.현재 API는 다음과 같이 사용됩니다.
int newcount = count + len; ensureCapacity(newcount);
오버플로를 피하려면 덜 자연스러운 것으로 변경해야합니다.
ensureCapacity(count, len); int newcount = count + len;
어쨌든, 나는 오버플로에 민감한 코드를 유지하고 있지만 경고 주석을 추가하고 거대한 배열 생성을 “아웃 라이닝”하여
ArrayList
코드는 다음과 같습니다./** * Increases the capacity of this <tt>ArrayList</tt> instance, if * necessary, to ensure that it can hold at least the number of elements * specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ public void ensureCapacity(int minCapacity) { modCount++; // Overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } /** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // Overflow-conscious code int oldCapacity = elementData.length; 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); } private int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
Webrev가 재생성되었습니다.
남자 이름
Java 6에서 API를 다음과 같이 사용하는 경우 :
int newcount = count + len;
ensureCapacity(newcount);
그리고 newCount
넘침 (이것은 음수가 됨), if (minCapacity > oldCapacity)
거짓을 반환하고 실수로 ArrayList
가 증가 했다고 가정 할 수 있습니다 len
.
답변
코드를 보면 :
int newCapacity = oldCapacity + (oldCapacity >> 1);
경우 oldCapacity
매우 큰,이 오버 플로우되며, newCapacity
음의 수있을 것입니다. 과 같은 비교 newCapacity < oldCapacity
는 잘못 평가 true
하고 ArrayList
성장하지 못할 것입니다.
대신, 작성된 코드 ( newCapacity - minCapacity < 0
false를 반환 함)는 newCapacity
다음 행에서 음수 값을 추가로 평가할 수 있도록하여 ( )를 newCapacity
호출하여을 hugeCapacity
(를 newCapacity = hugeCapacity(minCapacity);
) ArrayList
증가시킬 수 있도록 하여 다시 계산 합니다 MAX_ARRAY_SIZE
.
이것은 무엇 // overflow-conscious code
다소 비스듬하지만 댓글, 의사 소통을 시도하고있다.
결론적으로, 새로운 비교 ArrayList
는 사전 정의 된 것보다 더 큰 것을 할당하지 MAX_ARRAY_SIZE
않으면 서 필요한 경우 바로 그 한계까지 커질 수 있습니다.
답변
두 가지 형식은식이 a - b
오버플로 되지 않는 한 정확히 동일하게 동작하며 ,이 경우에는 반대입니다. 경우 a
큰 음, 그리고 b
큰 긍정적, 다음 (a < b)
분명 사실이지만, a - b
그렇게 긍정적이 될 오버플로 (a - b < 0)
false입니다.
x86 어셈블리 코드에 익숙한 경우 SF = OF 일 때 if 문의 본문을 분기 하는 (a < b)
로 구현되는 것을 고려 jge
하십시오. 반면에 SF = 0 일 때 분기되는 (a - b < 0)
a처럼 jns
작동합니다. 따라서 OF = 1 일 때는 다르게 동작합니다.