Ich las Javas ArrayList
Quellcode und bemerkte einige Vergleiche in if-Anweisungen.
In Java 7 werden die Verfahren grow(int)
Verwendungen
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
In Java 6 grow
nicht vorhanden. Die Methode verwendet ensureCapacity(int)
jedoch
if (newCapacity < minCapacity)
newCapacity = minCapacity;
Was war der Grund für die Änderung? War es ein Leistungsproblem oder nur ein Stil?
Ich könnte mir vorstellen, dass der Vergleich mit Null schneller ist, aber eine vollständige Subtraktion durchzuführen, um zu überprüfen, ob sie negativ ist, scheint mir ein bisschen übertrieben. Auch in Bezug auf den Bytecode würde dies zwei Anweisungen ( ISUB
und IF_ICMPGE
) anstelle von einer ( IFGE
) beinhalten.
java
if-statement
arraylist
Dejvuth
quelle
quelle
if (newCapacity - minCapacity < 0)
besser alsif (newCapacity < minCapacity)
Überlauf zu verhindern?Antworten:
a < b
unda - b < 0
kann zwei verschiedene Dinge bedeuten. Betrachten Sie den folgenden Code:Beim Ausführen wird nur gedruckt
a - b < 0
. Was passiert ist, dassa < b
das eindeutig falsch ist, abera - b
überläuft und wird-1
, was negativ ist.Bedenken Sie nun, dass das Array eine Länge hat, die wirklich nahe liegt
Integer.MAX_VALUE
. Der Code inArrayList
lautet wie folgt:oldCapacity
ist wirklich nah anInteger.MAX_VALUE
sonewCapacity
(was istoldCapacity + 0.5 * oldCapacity
) könnte überlaufen und werdenInteger.MIN_VALUE
(dh negativ). Dann subtrahiert man dieminCapacity
Unterläufe zurück in eine positive Zahl.Diese Prüfung stellt sicher, dass die
if
nicht ausgeführt wird. Wenn der Code als geschriebenif (newCapacity < minCapacity)
wäre, wäre estrue
in diesem Fall (danewCapacity
ist negativ), so dass dernewCapacity
gezwungen wäre,minCapacity
unabhängig von deroldCapacity
.Dieser Überlauffall wird vom nächsten if behandelt. Wenn
newCapacity
übergelaufen ist, ist diestrue
:MAX_ARRAY_SIZE
definiert alsInteger.MAX_VALUE - 8
undInteger.MIN_VALUE - (Integer.MAX_VALUE - 8) > 0
isttrue
. DasnewCapacity
wird daher richtig gehandhabt:hugeCapacity
Methode gibtMAX_ARRAY_SIZE
oder zurückInteger.MAX_VALUE
.NB: das ist was die
// overflow-conscious code
Kommentar in dieser Methode sagt.quelle
a - b
und prüfen, ob das oberste Bit a ist1
. Wie gehen sie mit Überlauf um?Ich habe diese Erklärung gefunden :
Wenn Sie in Java 6 die API verwenden als:
Und
newCount
Überläufe (dies wird negativ)if (minCapacity > oldCapacity)
geben false zurück und Sie können fälschlicherweise annehmen, dass der Wert umArrayList
erhöht wurdelen
.quelle
ensureCapacity
widersprochen ; Wenn diesminCapacity
negativ ist, gelangen Sie nie an diesen Punkt - es wird genauso stillschweigend ignoriert, wie die komplizierte Implementierung vorgibt, dies zu verhindern. "Wir können dies nicht tun" für die Kompatibilität mit öffentlichen APIs ist daher ein seltsames Argument, wie sie es bereits getan haben. Die einzigen Anrufer, die sich auf dieses Verhalten verlassen, sind die internen.minCapacity
ist sehr negativ (dh resultiert aus einemint
Überlauf beim Hinzufügen der aktuellen Größe der ArrayList zur Anzahl der Elemente, die Sie hinzufügen möchten ),minCapacity - elementData.length
um erneut überzulaufen und positiv zu werden. So verstehe ich es.if (minCapacity > minExpand)
, was ich nicht verstehe.addAll
Methoden sind der einzige Fall, in dem dies relevant ist, da die Summe aus aktueller Größe und Anzahl neuer Elemente überlaufen kann. Dies sind jedoch interne Aufrufe, und das Argument „Wir können es nicht ändern, weilensureCapacity
es sich um eine öffentliche API handelt“ ist ein seltsames Argument,ensureCapacity
obwohl negative Werte tatsächlich ignoriert werden. Die Java 8-API hat dieses Verhalten nicht geändert. Sie ignoriert lediglich Kapazitäten unterhalb der Standardkapazität, wennArrayList
sie sich im Anfangszustand befindet (dh mit Standardkapazität initialisiert und noch leer).newcount = count + len
ist richtig, wenn es um den internen Gebrauch geht, sie gilt jedoch nicht für diepublic
MethodeensureCapacity()
…Den Code betrachten:
Wenn
oldCapacity
es ziemlich groß ist, läuft es über undnewCapacity
ist eine negative Zahl. Ein Vergleich wienewCapacity < oldCapacity
wird falsch bewertettrue
und derArrayList
wird nicht wachsen.Stattdessen ermöglicht der geschriebene Code (
newCapacity - minCapacity < 0
gibt false zurück), dass der negative Wert vonnewCapacity
in der nächsten Zeile weiter ausgewertet wird, was zu einer NeuberechnungnewCapacity
durch Aufrufen vonhugeCapacity
(newCapacity = hugeCapacity(minCapacity);
) führt, damit der Wert bis zuArrayList
wachsen kannMAX_ARRAY_SIZE
.Dies ist, was der
// overflow-conscious code
Kommentar zu kommunizieren versucht, wenn auch eher schräg.Unterm Strich schützt der neue Vergleich also davor, einen
ArrayList
größeren als den vordefinierten zuzuweisenMAX_ARRAY_SIZE
während es bei Bedarf bis zu diesem Grenzwert wachsen kann.quelle
Die beiden Formen verhalten sich genau gleich, es sei denn, der Ausdruck
a - b
läuft über. In diesem Fall sind sie entgegengesetzt. Wenna
es ein großes Negativ undb
ein großes Positiv ist, dann(a < b)
ist dies eindeutig wahr, wird abera - b
überlaufen, um positiv zu werden(a - b < 0)
ist daher falsch.Wenn Sie mit x86-Assemblycode vertraut sind, beachten Sie, dass dieser
(a < b)
durch a implementiert wirdjge
, der sich um den Hauptteil der if-Anweisung verzweigt, wenn SF = OF. Auf der anderen Seite verhält es sich(a - b < 0)
wie ajns
, das sich verzweigt, wenn SF = 0. Daher verhalten sich diese genau anders, wenn OF = 1.quelle