Czytałem ArrayList
kod źródłowy Javy i zauważyłem pewne porównania w instrukcjach if.
W Javie 7 metoda grow(int)
wykorzystuje
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
W Javie 6 grow
nie istniał. Metoda ensureCapacity(int)
jednak wykorzystuje
if (newCapacity < minCapacity)
newCapacity = minCapacity;
Jaki był powód zmiany? Czy to był problem z wydajnością, czy tylko styl?
Mogę sobie wyobrazić, że porównywanie z zerem jest szybsze, ale wykonanie pełnego odejmowania tylko po to, aby sprawdzić, czy jest ujemne, wydaje mi się nieco przesadne. Również pod względem kodu bajtowego wymagałoby to dwóch instrukcji ( ISUB
i IF_ICMPGE
) zamiast jednej ( IFGE
).
java
if-statement
arraylist
dejvuth
źródło
źródło
if (newCapacity - minCapacity < 0)
lepsze niżif (newCapacity < minCapacity)
zapobieganie przepełnieniu?Odpowiedzi:
a < b
ia - b < 0
może oznaczać dwie różne rzeczy. Rozważ następujący kod:Po uruchomieniu zostanie to wydrukowane
a - b < 0
. To, co się dzieje,a < b
jest wyraźnie fałszywe, alea - b
przelewa się i staje-1
, co jest negatywne.Powiedziawszy to, zastanów się, że tablica ma długość, która jest naprawdę bliska
Integer.MAX_VALUE
. KodArrayList
wygląda następująco:oldCapacity
jest naprawdę blisko,Integer.MAX_VALUE
więcnewCapacity
(który jestoldCapacity + 0.5 * oldCapacity
) może się przepełnić i staćInteger.MIN_VALUE
(tj. ujemny). Następnie odejmującminCapacity
niedomiar z powrotem do liczby dodatniej.To sprawdzenie zapewnia, że
if
nie zostanie wykonane. Gdyby kod został napisany jakoif (newCapacity < minCapacity)
, byłbytrue
w tym przypadku (ponieważnewCapacity
jest ujemny), więcnewCapacity
byłby zmuszony dominCapacity
niezależnie odoldCapacity
.Ten przypadek przepełnienia jest obsługiwany przez następny if. Kiedy
newCapacity
się przepełni, będzie totrue
:MAX_ARRAY_SIZE
jest zdefiniowane jakoInteger.MAX_VALUE - 8
iInteger.MIN_VALUE - (Integer.MAX_VALUE - 8) > 0
jesttrue
. DlategonewCapacity
jest odpowiednio obsługiwane:hugeCapacity
metoda zwracaMAX_ARRAY_SIZE
lubInteger.MAX_VALUE
.Uwaga: tak mówi
// overflow-conscious code
komentarz w tej metodzie.źródło
a - b
znakiem, wykonując i sprawdzając, czy górny bit to1
. Jak radzą sobie z przepełnieniem?Znalazłem to wyjaśnienie :
W Javie 6, jeśli używasz interfejsu API jako:
I
newCount
przepełnienia (to staje się ujemna),if (minCapacity > oldCapacity)
zwróci false i może błędnie zakładają, żeArrayList
została zwiększona olen
.źródło
ensureCapacity
; jeśliminCapacity
jest negatywne, nigdy nie dochodzisz do tego punktu - jest tak samo cicho ignorowane, jak skomplikowane wdrożenie udaje, aby temu zapobiec. Zatem „nie możemy tego zrobić” dla zgodności publicznego interfejsu API jest dziwnym argumentem, tak jak już to zrobili. Jedynymi osobami wywołującymi to zachowanie są osoby wewnętrzne.minCapacity
jest bardzo ujemny (tj. Wynika zint
przepełnienia podczas dodawania bieżącego rozmiaru ArrayList do liczby elementów, które chcesz dodać),minCapacity - elementData.length
aby ponownie przepełnić i stać się dodatni. Tak to rozumiem.if (minCapacity > minExpand)
, czego nie rozumiem.addAll
metody są jedynym przypadkiem, w którym jest to istotne, ponieważ suma bieżącego rozmiaru i liczby nowych elementów może się przepełnić. Niemniej jednak są to wywołania wewnętrzne, a argument „nie możemy tego zmienić, ponieważensureCapacity
jest publicznym interfejsem API” jest dziwnym argumentem, gdy w rzeczywistościensureCapacity
ignoruje wartości ujemne. Interfejs API Java 8 nie zmienił tego zachowania, wszystko to ignoruje pojemności poniżej pojemności domyślnej, gdyArrayList
jest w stanie początkowym (tj. Zainicjowany z domyślną pojemnością i nadal pusty).newcount = count + len
zastosowania wewnętrznego jest poprawne, jednak nie dotyczy onopublic
metodyensureCapacity()
…Patrząc na kod:
Jeśli
oldCapacity
jest dość duży, to się przepełni inewCapacity
będzie liczbą ujemną. Takie porównanienewCapacity < oldCapacity
źle ocenitrue
iArrayList
nie będzie rosło.Zamiast tego zapisany kod (
newCapacity - minCapacity < 0
zwraca false) pozwoli nanewCapacity
dalszą ocenę wartości ujemnej w następnym wierszu, co spowoduje ponowne obliczenienewCapacity
przez wywołaniehugeCapacity
(newCapacity = hugeCapacity(minCapacity);
), aby umożliwićArrayList
wzrostMAX_ARRAY_SIZE
.To właśnie
// overflow-conscious code
komentarz próbuje przekazać, choć raczej skośnie.Podsumowując , nowe porównanie chroni przed przydzieleniem wartości
ArrayList
większej niż wstępnie zdefiniowana,MAX_ARRAY_SIZE
jednocześnie umożliwiając jej wzrost do tego limitu w razie potrzeby.źródło
Obie formy zachowują się dokładnie tak samo, chyba że wyrażenie się
a - b
przepełni, w którym to przypadku są przeciwne. Jeślia
jest dużym ujemnym ib
jest dużym dodatnim, to(a < b)
jest to oczywiście prawda, alea - b
przepełni się, aby stać się dodatnim, więc(a - b < 0)
jest fałszywe.Jeśli znasz kod zestawu x86, zastanów się, że
(a < b)
jest on implementowany przez ajge
, który rozgałęzia się wokół treści instrukcji if, gdy SF = OF. Z drugiej strony(a - b < 0)
będzie zachowywał się jak ajns
, który rozgałęzia się, gdy SF = 0. Stąd zachowują się inaczej dokładnie, gdy OF = 1.źródło