[java] if (a-b <0)와 if (a <b)의 차이점

Java의 ArrayList소스 코드를 읽고 if 문에서 몇 가지 비교를 발견했습니다.

Java 7에서이 방법 grow(int)

if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;

Java 6 grow에는 존재하지 않았습니다. ensureCapacity(int)그러나이 방법 은

if (newCapacity < minCapacity)
    newCapacity = minCapacity;

변화의 원인은 무엇입니까? 성능 문제입니까 아니면 스타일입니까?

나는 0과 비교하는 것이 더 빠르다고 생각할 수 있지만, 음수인지 확인하기 위해 완전한 빼기를 수행하는 것은 나에게 약간 과잉 것 같습니다. 또한 바이트 코드와 관련 하여 하나 ( ) 대신 두 개의 명령어 ( ISUBIF_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 < 0false를 반환 함)는 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 일 때는 다르게 동작합니다.


답변