나는 이해하지 못하는 작은 구현 세부 사항 질문이 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
, 매번 I 호 remove
, 하부는 ArrayList
내부 배열 실제로 변경할 것을 의미하는 "좋은"상태로하게되어야한다. ,의 모든 단일 호출마다 내부적 remove
으로 호출이 있습니다 System::arrayCopy
.
대조적 removeIf
으로 더 똑똑합니다. 내부적으로 반복을 수행하므로 작업을보다 최적화 할 수 있습니다. 이것이하는 방식은 흥미 롭습니다.
먼저 요소를 제거 해야하는 인덱스를 계산합니다. 이것은 먼저 각 인덱스에서 값 (a )가 있는 작은 값 BitSet
의 배열을 계산하여 수행됩니다 . 여러 값이 있으면이를 a로 만듭니다. 특정 오프셋에서 값을 설정하려면 먼저 배열에서 인덱스를 찾은 다음 해당 비트를 설정해야합니다. 이것은 매우 복잡하지 않습니다. 비트 65와 3을 설정한다고 가정 해 봅시다. 먼저 64 비트를 넘었지만 128을 넘지 않기 때문에 a가 필요합니다 .long
64 bit
long
64 bit
BitSet
long [] l = new long[2]
|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|
소스 코드에서이 비트 세트 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
이 원래보다 훨씬 나아 보이지는 않습니다. 전체 단어를 건너 뛸 수있는 기회가 빠졌습니다.