У меня есть небольшой вопрос о реализации, который я не могу понять в ArrayList::removeIf . Я не думаю, что я могу просто сказать так, как есть, без каких-либо предварительных условий.
Как таковой: реализация в основном большая 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);
И я хотел бы удалить каждый элемент, который является четным. Я мог бы сделать:
Iterator<Integer> iter = list.iterator();
while (iter.hasNext()) {
int elem = iter.next();
if (elem % 2 == 0) {
iter.remove();
}
}
Или :
list.removeIf(x -> x % 2 == 0);
Результат будет таким же, но реализация будет отличаться. Поскольку iteratorпредставление является видом ArrayList, каждый раз, когда я вызываю remove, базовый уровень ArrayListдолжен быть приведен в «хорошее» состояние, означающее, что внутренний массив действительно изменится. Опять же, при каждом вызове remove, будут звонкиSystem::arrayCopy внутренние .
На контрасте removeIfумнее. Поскольку он выполняет итерацию внутри, он может сделать вещи более оптимизированными. То, как это происходит, интересно.
Сначала он вычисляет индексы, из которых элементы должны быть удалены. Это делается сначала путем вычисления крошечного BitSetмассива longзначений, где в каждом индексе находится 64 bitзначение (a long). Множественные 64 bitзначения делают это BitSet. Чтобы установить значение с определенным смещением, сначала нужно найти индекс в массиве, а затем установить соответствующий бит. Это не очень сложно. Допустим, вы хотите установить биты 65 и 3. Сначала нам нужно long [] l = new long[2](потому что мы вышли за пределы 64 бит, но не более 128):
|0...(60 more bits here)...000|0...(60 more bits here)...000|
Сначала вы находите индекс: 65 / 64(они на самом деле делают 65 >> 6), а затем в этом индексе ( 1) помещаете нужный бит:
1L << 65 // this will "jump" the first 64 bits, so this will actually become 00000...10.
То же самое для 3. Таким образом, длинный массив станет:
|0...(60 more bits here)...010|0...(60 more bits here)...1000|
В исходном коде они называют этот BitSet - deathRow (красивое имя!).
Давайте возьмем это even пример здесь, гдеlist = 2, 4, 6, 5, 5
- они перебирают массив и вычисляют это
deathRow(гдеPredicate::testестьtrue).
deathRow = 7 (000 ... 111)
означающие индексы = [0, 1, 2] должны быть удалены
- теперь они заменяют элементы в базовом массиве на основе этого deathRow (не вдаваясь в детали, как это делается)
Внутренний массив становится: [5, 5, 6, 5, 5]. В основном они перемещают элементы, которые должны оставаться перед массивом.
Я наконец могу внести в вопрос.
На данный момент они знают:
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
Для меня здесь есть один шаг:
void getRidOfElementsFromWToEnd() {
for(int i=w; i<end; ++i){
es[i] = null;
}
size = w;
}
Вместо этого это происходит:
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;
}
Я специально переименовал переменные здесь.
Какой смысл звонить:
System.arraycopy(es, end, es, w, size - end);
Тем более size - end, что end это size все время - оно никогда не меняется (так всегда zero). Это в основном NO-OP здесь. В каком угловом случае я здесь скучаю?
System.arraycopy(es, end, es, w, size - end)качестве основных деталей реализации removeIf? Я почти чувствовал, что я читал ответ на какой-то другой вопрос между ними. (Читая комментарий выше) Я чувствую, что в конечном итоге он оказался в тривиальном вопросе. Это так?
System.arrayCopy. Тем не менее, это было забавное путешествие через детали (этот внутренний набор битов имеет ту же идею, что и java.util.BitSet)
range...), и я приму его.
java.util.BitSet. Для меня повторная реализация BitSetопераций не выглядит значительно лучше, чем оригинал. Возможность пропустить целые слова была упущена.