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:remove
ArrayList::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 iterator
bir görünümüdür ArrayList
, her zaman çağrı remove
, altında yatan ArrayList
iç dizisi aslında değişecek, yani "iyi" bir duruma getirilmesi gerekir. Yine, her remove
çağrısında System::arrayCopy
dahili olarak çağrılar yapılacaktır .
Kontrast removeIf
daha 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 BitSet
dizisi 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):long
64 bit
long
64 bit
BitSet
long [] 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::test
olduğ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 - end
beri 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, BitSet
operasyonların yeniden uygulanması orijinalinden daha iyi görünmüyor. Tüm kelimeleri atlama fırsatı kaçırıldı.