Я читал ArrayList
исходный код Java и заметил некоторые сравнения в операторах if.
В Java 7 метод grow(int)
использует
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
В Java 6 grow
не существовало. Однако метод ensureCapacity(int)
использует
if (newCapacity < minCapacity)
newCapacity = minCapacity;
Что было причиной изменения? Это была проблема с производительностью или просто стиль?
Я мог бы предположить, что сравнение с нулем происходит быстрее, но выполнение полного вычитания, просто чтобы проверить, является ли оно отрицательным, кажется мне немного излишним. Также с точки зрения байт-кода, это будет включать две инструкции ( ISUB
и IF_ICMPGE
) вместо одной ( IFGE
).
java
if-statement
arraylist
dejvuth
источник
источник
if (newCapacity - minCapacity < 0)
лучше, чемif (newCapacity < minCapacity)
с точки зрения предотвращения переполнения?Ответы:
a < b
иa - b < 0
может означать две разные вещи. Рассмотрим следующий код:При запуске это будет только печатать
a - b < 0
. То, что происходит, - то, чтоa < b
явно ложно, ноa - b
переполняется и становится-1
отрицательным.Теперь, сказав это, учтите, что массив имеет длину, очень близкую к
Integer.MAX_VALUE
. КодArrayList
выглядит так: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
комментарий в этом методе.источник
a - b
и проверяя, является ли старший бит a1
. Как они справляются с переполнением?Я нашел это объяснение :
В Java 6, если вы используете API как:
И
newCount
переполнения (это становится отрицательным),if (minCapacity > oldCapacity)
вернет false, и вы можете ошибочно предположить, чтоArrayList
был увеличен наlen
.источник
ensureCapacity
; еслиminCapacity
оно отрицательное, вы никогда не достигнете этой точки - оно так же тихо игнорируется, как и сложная реализация, которая пытается предотвратить. Так что «мы не можем этого сделать» для совместимости с публичным API - странный аргумент, как они уже сделали. Единственные абоненты, полагающиеся на это поведение, являются внутренними.minCapacity
очень отрицательное (т. Е. Возникло из-заint
переполнения при добавлении текущего размера ArrayList к числу элементов, которые вы хотите добавить),minCapacity - elementData.length
снова переполниться и стать положительным. Вот как я это понимаю.if (minCapacity > minExpand)
что я не понимаю.addAll
метода являются единственным случаем, когда это уместно, так как сумма текущего размера и количества новых элементов может переполниться. Тем не менее, это внутренние вызовы, и аргумент «мы не можем его изменить, потому чтоensureCapacity
это публичный API» является странным аргументом, когда фактическиensureCapacity
игнорирует отрицательные значения. API-интерфейс Java 8 не изменил это поведение, все, что он делает, это игнорирует возможности ниже емкости по умолчанию, когда онArrayList
находится в своем начальном состоянии (то есть инициализирован с емкостью по умолчанию и все еще пуст).newcount = count + len
том, что правильно, когда речь идет о внутреннем использовании, однако, оно не относится кpublic
методуensureCapacity()
...Глядя на код:
Если
oldCapacity
оно достаточно велико, оно будет переполнено иnewCapacity
будет отрицательным числом. Сравнение вродеnewCapacity < oldCapacity
будет неправильно оцениватьtrue
иArrayList
расти не будет.Вместо этого код в том виде, в котором он написан (
newCapacity - minCapacity < 0
возвращает false), позволитnewCapacity
дополнительно оценить отрицательное значение в следующей строке, что приведет к повторному вычислению сnewCapacity
помощью invokinghugeCapacity
(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)
что ложно.Если вы знакомы с ассемблерным кодом x86, подумайте, что
(a < b)
он реализован с помощью ajge
, который разветвляется вокруг тела оператора if, когда SF = OF. С другой стороны,(a - b < 0)
будет действовать как ajns
, который разветвляется, когда SF = 0. Следовательно, они ведут себя по-разному точно, когда OF = 1.источник