我有一个小的实施细节问题,我在中无法理解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
操作看起来并不比原始操作好得多。错过了跳过整个单词的机会。