Ho una piccola domanda sui dettagli di implementazione che non riesco a capire ArrayList::removeIf. Non credo di poter semplicemente dirlo così com'è senza prima alcune condizioni preliminari.
Come tale: l'implementazione è sostanzialmente una massa remove , a differenza ArrayList::remove. Un esempio dovrebbe rendere le cose molto più facili da capire. Diciamo che ho questo elenco:
List<Integer> list = new ArrayList<>(); // 2, 4, 6, 5, 5
list.add(2);
list.add(4);
list.add(6);
list.add(5);
list.add(5);
E vorrei rimuovere ogni elemento uniforme. Potrei fare:
Iterator<Integer> iter = list.iterator();
while (iter.hasNext()) {
int elem = iter.next();
if (elem % 2 == 0) {
iter.remove();
}
}
Oppure :
list.removeIf(x -> x % 2 == 0);
Il risultato sarà lo stesso, ma l'implementazione è molto diversa. Dal momento che iteratorè una visione di ArrayList, ogni volta che chiamo remove, il sottostante ArrayListdeve essere portato in uno stato "buono", il che significa che l'array interno cambierà effettivamente. Ancora una volta, ad ogni singola chiamata di remove, ci saranno chiamate System::arrayCopyinternamente.
Al contrario removeIfè più intelligente. Dal momento che esegue l'iterazione internamente, può rendere le cose più ottimizzate. Il modo in cui lo fa è interessante.
Calcola innanzitutto gli indici da cui si suppone che gli elementi vengano rimossi. Questo viene fatto calcolando prima un valore minuscolo BitSet, una matrice di longvalori in cui in ciascun indice risiede un 64 bitvalore (a long). 64 bitValori multipli rendono questo un BitSet. Per impostare un valore su un determinato offset, è innanzitutto necessario individuare l'indice nell'array e quindi impostare il bit corrispondente. Questo non è molto complicato. Supponiamo che tu voglia impostare i bit 65 e 3. Innanzitutto abbiamo bisogno di un long [] l = new long[2](perché siamo andati oltre i 64 bit, ma non più di 128):
|0...(60 more bits here)...000|0...(60 more bits here)...000|
Prima trovi l'indice: 65 / 64(lo fanno effettivamente 65 >> 6) e poi in quell'indice ( 1) inserisci il bit necessario:
1L << 65 // this will "jump" the first 64 bits, so this will actually become 00000...10.
Stessa cosa per 3. Di conseguenza, tale array lungo diventerà:
|0...(60 more bits here)...010|0...(60 more bits here)...1000|
Nel codice sorgente chiamano questo BitSet - deathRow(bel nome!).
Facciamo questo evenesempio qui, dovelist = 2, 4, 6, 5, 5
- ripetono l'array e lo calcolano
deathRow(dove siPredicate::testtrovatrue).
deathRow = 7 (000 ... 111)
significa che gli indici = [0, 1, 2] devono essere rimossi
- ora sostituiscono gli elementi nell'array sottostante basato su quel deathRow (non entrando nei dettagli come questo è fatto)
l'array interno diventa: [5, 5, 6, 5, 5]. Fondamentalmente spostano gli elementi che dovrebbero rimanere di fronte alla matrice.
Posso finalmente portare la domanda.
A questo punto, sanno:
w -> number of elements that have to remain in the list (2)
es -> the array itself ([5, 5, 6, 5, 5])
end -> equal to size, never changed
Per me, c'è un solo passo da fare qui:
void getRidOfElementsFromWToEnd() {
for(int i=w; i<end; ++i){
es[i] = null;
}
size = w;
}
Invece, questo accade:
private void shiftTailOverGap(Object[] es, int w, int end) {
System.arraycopy(es, end, es, w, size - end);
for (int to = size, i = (size -= end - w); i < to; i++)
es[i] = null;
}
Ho rinominato le variabili di proposito qui.
Qual è il punto nella chiamata:
System.arraycopy(es, end, es, w, size - end);
Soprattutto size - end, dato che end è size tutto il tempo - non è mai cambiato (quindi questo è sempre zero). Questo è fondamentalmente un NO-OP qui. Quale caso d'angolo mi manca qui?
System.arraycopy(es, end, es, w, size - end)come i dettagli di implementazione sottostanti di removeIf? Mi sembrava quasi di leggere una risposta a qualche altra domanda in mezzo. (Leggendo il commento sopra) Sento che alla fine è finita in una domanda banale. È così?
System.arrayCopy. Tuttavia è stato un viaggio divertente attraverso i dettagli (quel set di bit interno risulta avere la stessa idea di java.util.BitSet)
range...) e lo accetterò.
java.util.BitSet. Per me, la reimplementazione delle BitSetoperazioni non sembra significativamente migliore dell'originale. L'opportunità di saltare intere parole è stata persa.