Saya punya pertanyaan detail implementasi kecil yang gagal saya pahami ArrayList::removeIf. Saya tidak berpikir saya bisa mengatakannya seperti itu tanpa prasyarat terlebih dahulu.
Seperti: pelaksanaan pada dasarnya adalah sebuah massal remove , tidak seperti ArrayList::remove. Sebuah contoh seharusnya membuat banyak hal lebih mudah untuk dipahami. Katakanlah saya memiliki daftar ini:
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);
Dan saya ingin menghapus setiap elemen yang genap. Saya bisa melakukan:
Iterator<Integer> iter = list.iterator();
while (iter.hasNext()) {
int elem = iter.next();
if (elem % 2 == 0) {
iter.remove();
}
}
Atau :
list.removeIf(x -> x % 2 == 0);
Hasilnya akan sama, tetapi implementasinya sangat berbeda. Karena iteratorini adalah pandangan dari ArrayList, setiap kali saya menelepon remove, yang mendasarinya ArrayListharus dibawa ke keadaan "baik", yang berarti bahwa array batin sebenarnya akan berubah. Sekali lagi, pada setiap panggilan tunggal remove, akan ada panggilan System::arrayCopyinternal.
Sebaliknya removeIflebih pintar. Karena ia melakukan iterasi secara internal, itu dapat membuat hal-hal lebih optimal. Cara melakukan ini menarik.
Ini pertama menghitung indeks di mana elemen seharusnya dihapus. Ini dilakukan dengan terlebih dahulu menghitung kecil BitSet, array longnilai di mana pada setiap indeks, berada suatu 64 bitnilai (a long). Banyak 64 bitnilai membuat ini a BitSet. Untuk menetapkan nilai pada offset tertentu, pertama-tama Anda perlu mengetahui indeks dalam array dan kemudian mengatur bit yang sesuai. Ini tidak terlalu rumit. Katakanlah Anda ingin mengatur bit 65 dan 3. Pertama kita membutuhkan long [] l = new long[2](karena kita melampaui 64 bit, tetapi tidak lebih dari 128 bit):
|0...(60 more bits here)...000|0...(60 more bits here)...000|
Pertama-tama Anda menemukan indeks: 65 / 64(mereka benar-benar melakukannya 65 >> 6) dan kemudian dalam indeks itu ( 1) masukkan bit yang diperlukan:
1L << 65 // this will "jump" the first 64 bits, so this will actually become 00000...10.
Hal yang sama untuk 3. Dengan demikian array panjang itu akan menjadi:
|0...(60 more bits here)...010|0...(60 more bits here)...1000|
Dalam kode sumber, mereka menyebut BitSet ini - deathRow(nama yang bagus!).
Mari kita ambil evencontoh itu di sini, di manalist = 2, 4, 6, 5, 5
- mereka iterate array dan menghitung ini
deathRow(di manaPredicate::testadalahtrue).
deathRow = 7 (000 ... 111)
arti indeks = [0, 1, 2] harus dihapus
- mereka sekarang mengganti elemen dalam array yang mendasari berdasarkan deathRow itu (tidak masuk ke detail bagaimana ini dilakukan)
array batin menjadi: [5, 5, 6, 5, 5]. Pada dasarnya mereka memindahkan elemen yang seharusnya tetap berada di depan array.
Saya akhirnya bisa mengajukan pertanyaan.
Pada titik ini, mereka tahu:
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
Bagi saya, ada satu langkah yang harus dilakukan di sini:
void getRidOfElementsFromWToEnd() {
for(int i=w; i<end; ++i){
es[i] = null;
}
size = w;
}
Sebaliknya, ini terjadi:
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;
}
Saya telah mengganti nama variabel dengan sengaja di sini.
Apa gunanya menelepon:
System.arraycopy(es, end, es, w, size - end);
Terutama size - end, karena end selalu size - tidak pernah berubah (jadi ini selalu zero). Ini pada dasarnya adalah NO-OP di sini. Kasus sudut apa yang saya lewatkan di sini?
System.arraycopy(es, end, es, w, size - end)sebagai detail implementasi yang mendasarinya removeIf? Aku hampir merasa seperti, aku sedang membaca jawaban untuk beberapa pertanyaan lain di antaranya. (Membaca komentar di atas) Saya merasakannya berakhir pada pertanyaan sepele akhirnya. Apakah begitu?
System.arrayCopy. Namun demikian itu adalah perjalanan yang menyenangkan melalui detail (yang ditetapkan bit internal ternyata memiliki ide yang sama seperti java.util.BitSet)
range...) dan saya akan menerimanya.
java.util.BitSet. Bagi saya, implementasi ulang BitSetoperasi tidak terlihat jauh lebih baik daripada yang asli. Peluang untuk melewatkan seluruh kata telah terlewatkan.