Tôi có một câu hỏi chi tiết thực hiện nhỏ mà tôi không hiểu được ArrayList::removeIf
. Tôi không nghĩ rằng tôi có thể đơn giản đặt nó theo cách mà không có một số điều kiện tiên quyết.
Như vậy: việc thực hiện về cơ bản là một số lượng lớn remove
, không giống như ArrayList::remove
. Một ví dụ sẽ làm cho mọi thứ dễ hiểu hơn nhiều. Hãy nói rằng tôi có danh sách này:
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);
Và tôi muốn loại bỏ mọi yếu tố đồng đều. Tôi có thể làm:
Iterator<Integer> iter = list.iterator();
while (iter.hasNext()) {
int elem = iter.next();
if (elem % 2 == 0) {
iter.remove();
}
}
Hoặc :
list.removeIf(x -> x % 2 == 0);
Kết quả sẽ giống nhau, nhưng việc thực hiện rất khác nhau. Vì iterator
quan điểm của ArrayList
mỗi lần tôi gọi remove
, phần bên dưới ArrayList
phải được đưa đến trạng thái "tốt", nghĩa là mảng bên trong sẽ thực sự thay đổi. Một lần nữa, trên mỗi cuộc gọi của remove
, sẽ có các cuộc gọi System::arrayCopy
nội bộ.
Trên sự tương phản removeIf
là thông minh hơn. Vì nó lặp đi lặp lại bên trong, nó có thể làm cho mọi thứ được tối ưu hóa hơn. Cách nó làm điều này là thú vị.
Đầu tiên, nó tính toán các chỉ mục nơi các yếu tố được cho là bị xóa khỏi. Điều này được thực hiện bằng cách tính toán đầu tiên BitSet
một mảng nhỏ các long
giá trị trong đó tại mỗi chỉ mục, nằm trong một 64 bit
giá trị (a long
). Nhiều 64 bit
giá trị làm cho điều này a BitSet
. Để đặt một giá trị ở một mức bù cụ thể, trước tiên bạn cần tìm ra chỉ mục trong mảng và sau đó đặt bit tương ứng. Điều này không phức tạp lắm. Giả sử bạn muốn đặt bit 65 và 3. Trước tiên, chúng tôi cần một long [] l = new long[2]
(vì chúng tôi đã vượt quá 64 bit, nhưng không quá 128):
|0...(60 more bits here)...000|0...(60 more bits here)...000|
Trước tiên, bạn tìm thấy chỉ mục: 65 / 64
(họ thực sự làm 65 >> 6
) và sau đó trong chỉ mục đó ( 1
) đặt bit cần thiết:
1L << 65 // this will "jump" the first 64 bits, so this will actually become 00000...10.
Điều tương tự cho 3
. Như vậy, mảng dài sẽ trở thành:
|0...(60 more bits here)...010|0...(60 more bits here)...1000|
Trong mã nguồn, họ gọi đây là BitSet - deathRow
(tên hay!).
Hãy lấy even
ví dụ đó ở đây, nơilist = 2, 4, 6, 5, 5
- họ lặp mảng và tính này
deathRow
(nơiPredicate::test
làtrue
).
cái chếtRow = 7 (000 ... 111)
nghĩa là các chỉ mục = [0, 1, 2] sẽ bị xóa
- bây giờ họ thay thế các phần tử trong mảng cơ bản dựa trên deathRow đó (không đi sâu vào chi tiết cách thực hiện)
mảng bên trong trở thành: [5, 5, 6, 5, 5]. Về cơ bản, họ di chuyển các phần tử được cho là vẫn ở phía trước mảng.
Cuối cùng tôi có thể mang lại câu hỏi.
Tại thời điểm này, họ biết:
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
Đối với tôi, có một bước duy nhất để làm ở đây:
void getRidOfElementsFromWToEnd() {
for(int i=w; i<end; ++i){
es[i] = null;
}
size = w;
}
Thay vào đó, điều này xảy ra:
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;
}
Tôi đã đổi tên các mục đích ở đây.
Điểm gọi là gì:
System.arraycopy(es, end, es, w, size - end);
Đặc biệt size - end
, vì end
là size
tất cả thời gian - nó không bao giờ thay đổi (vì vậy điều này luôn luôn zero
). Đây cơ bản là một NO-OP ở đây. Trường hợp góc nào tôi bị thiếu ở đây?
System.arraycopy(es, end, es, w, size - end)
như là chi tiết thực hiện cơ bản của removeIf
? Tôi gần như cảm thấy, tôi đang đọc một câu trả lời cho một số câu hỏi khác ở giữa. (Đọc bình luận ở trên) Tôi cảm thấy cuối cùng trong một câu hỏi tầm thường. Là vậy sao?
System.arrayCopy
. Tuy nhiên, đó là một hành trình thú vị thông qua các chi tiết (bộ bit bên trong đó hóa ra có cùng ý tưởng với java.util.BitSet
)
range
...) và tôi sẽ chấp nhận nó.
java.util.BitSet
. Đối với tôi, việc thực hiện lại các BitSet
hoạt động không có vẻ tốt hơn đáng kể so với ban đầu. Cơ hội để bỏ qua toàn bộ từ đã bị bỏ lỡ.