Anlayamadığım küçük bir uygulama detay sorum var ArrayList::removeIf. Öncelikle bazı önkoşullar olmadan olduğu gibi koyabileceğimi sanmıyorum.
Bunun gibi: uygulama , aksine , temelde bir yığın . Bir örnek, işleri daha kolay anlaşılır yapmalıdır. Diyelim ki bu listeye sahibim:removeArrayList::remove
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);
Ve her şeyi bile kaldırmak istiyorum. Yapabilirim:
Iterator<Integer> iter = list.iterator();
while (iter.hasNext()) {
int elem = iter.next();
if (elem % 2 == 0) {
iter.remove();
}
}
Veya :
list.removeIf(x -> x % 2 == 0);
Sonuç aynı olacak, ancak uygulama çok farklı. Yana iteratorbir görünümüdür ArrayList, her zaman çağrı remove, altında yatan ArrayListiç dizisi aslında değişecek, yani "iyi" bir duruma getirilmesi gerekir. Yine, her removeçağrısında System::arrayCopydahili olarak çağrılar yapılacaktır .
Kontrast removeIfdaha akıllıdır. Yinelemeyi dahili olarak yaptığı için, işleri daha optimize edebilir. Bunu yapması ilginç.
İlk olarak elemanların kaldırılması gereken indeksleri hesaplar. Bu, önce her dizinde bir değer (a ) bulunduğu bir minik değer BitSetdizisi hesaplanarak yapılır . Birden çok değer bunu a yapar . Belirli bir ofsette bir değer ayarlamak için önce dizideki dizini bulmanız ve ardından karşılık gelen biti ayarlamanız gerekir. Bu çok karmaşık değil. Diyelim ki 65 ve 3. biti ayarlamak istiyorsunuz. Öncelikle bir ihtiyacımız var (çünkü 64 bitin ötesine geçtik, ancak 128'den fazla değil):long64 bitlong64 bitBitSetlong [] l = new long[2]
|0...(60 more bits here)...000|0...(60 more bits here)...000|
Önce dizini bulursunuz: 65 / 64(aslında yaparlar 65 >> 6) ve daha sonra bu dizine ( 1) gerekli biti koyun:
1L << 65 // this will "jump" the first 64 bits, so this will actually become 00000...10.
Aynı şey 3. Öyle ki, uzun dizi:
|0...(60 more bits here)...010|0...(60 more bits here)...1000|
Kaynak kodda buna BitSet - deathRow(güzel ad!) Diyorlar .
Bu evenörneği burada alalım , neredelist = 2, 4, 6, 5, 5
- bunlar dizi yineleme ve bu hesaplamak
deathRow(buradaPredicate::testolduğutrue).
deathRow = 7 (000 ... 111)
anlam indeksleri = [0, 1, 2] kaldırılacak
- artık bu ölüm sırasına göre temel dizideki öğeleri değiştiriyorlar (bunun nasıl yapıldığına dair ayrıntılara girmiyorlar)
iç dizi: [5, 5, 6, 5, 5] olur. Temelde dizinin önünde kalması gereken öğeleri taşırlar.
Sonunda soruyu getirebilirim.
Zamanın bu noktasında şunları biliyorlar:
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
Bana göre burada yapılacak tek bir adım var:
void getRidOfElementsFromWToEnd() {
for(int i=w; i<end; ++i){
es[i] = null;
}
size = w;
}
Bunun yerine, bu olur:
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;
}
Buradaki değişkenleri bilerek değiştirdim.
Seslenmenin anlamı nedir:
System.arraycopy(es, end, es, w, size - end);
Özellikle size - endberi end olduğu size her zaman - bu hiç değişmedi (bu her zaman o kadar zero). Bu temelde burada bir NO-OP. Burada hangi köşe davasını kaçırıyorum?
System.arraycopy(es, end, es, w, size - end)altında yatan uygulama ayrıntılarının kullanımı hakkında mıydı removeIf? Neredeyse hissettim, aradaki başka bir sorunun cevabını okuyordum. (Yukarıdaki yorumu okuyarak) Sonunda önemsiz bir soruya dönüştüğünü hissediyorum. Öyle mi?
System.arrayCopy. Bununla birlikte, ayrıntılar arasında eğlenceli bir yolculuktu (dahili bit seti aynı fikirle ortaya çıkıyor java.util.BitSet)
range...) ve kabul edeceğim.
java.util.BitSet. Bana göre, BitSetoperasyonların yeniden uygulanması orijinalinden daha iyi görünmüyor. Tüm kelimeleri atlama fırsatı kaçırıldı.