[Java] removeAll, retainAll ๋ด๋ถ ๋์ ๋ถ์, batchRemove
removeAll๊ณผ retainAll ๋ฉ์๋๋ ๋ชจ๋ ๋ด๋ถ์ ์ผ๋ก batchRemove ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉฐ, ์ ๊ฑฐ ๋ฐฉ์๋ง complement ํ๋ผ๋ฏธํฐ๋ก ๊ตฌ๋ถํ๋ค.
- removeAll: complement = false (์ปฌ๋ ์ ์ ์๋ ์์ ์ ๊ฑฐ)
- retainAll: complement = true (์ปฌ๋ ์ ์ ์๋ ์์๋ง ์ ์ง)
public boolean removeAll(Collection<?> c) {
return batchRemove(c, false, 0, size);
}
boolean batchRemove(Collection<?> c, boolean complement,
final int from, final int end) {
Objects.requireNonNull(c);
final Object[] es = elementData;
int r;
// Optimize for initial run of survivors
for (r = from;; r++) {
if (r == end)
return false;
if (c.contains(es[r]) != complement)
break;
}
int w = r++;
try {
for (Object e; r < end; r++)
if (c.contains(e = es[r]) == complement)
es[w++] = e;
} catch (Throwable ex) {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
System.arraycopy(es, r, es, w, end - r);
w += end - r;
throw ex;
} finally {
modCount += end - w;
shiftTailOverGap(es, w, end);
}
return true;
}
batchRemove ํต์ฌ ๋์
1. ์ฒซ ๋ฒ์งธ remove ๋์ ํ์
for (r = from;; r++) {
if (r == end)
return false;
if (c.contains(es[r]) != complement)
break;
}
ํด๋น ๋ฃจํ๋ ์ ๊ฑฐํ ํ์๊ฐ ์๋ ์ฐ์๋ ์์๋ฅผ ๊ฑด๋๋ฐ๋ ์ต์ ํ์ด๋ค. ์ฒซ ๋ฒ์งธ๋ก ์ ๊ฑฐ๋์ด์ผ ํ๋ ์์๋ฅผ ์ฐพ์ ๋๊น์ง ๋ฐ๋ณตํ๋ค.
2. remove ์ํ
int w = r++;
try {
for (Object e; r < end; r++)
if (c.contains(e = es[r]) == complement)
es[w++] = e;
}
- w: ์ฐ๊ธฐ ์์น ์ธ๋ฑ์ค (์ ์งํ ์์๋ฅผ ๋ณต์ฌํ ์์น)
- r: ์ฝ๊ธฐ ์์น ์ธ๋ฑ์ค (๊ฒ์ฌ ์ค์ธ ์์ ์์น)
r์ ์ฆ๊ฐ์ํค๋ฉฐ ๋ฐฐ์ด์์ ์กฐ๊ฑด์ ๋ง๋ ์์๊ฐ ๋ฐ๊ฒฌ๋๋ฉด w ์์น์ ๋ณต์ฌํ๊ณ w๋ฅผ ์ฆ๊ฐ์ํจ๋ค. (์ฆ, ์กฐ๊ฑด์ ๋ง์ง ์๋ ์์๋ ์คํต)
์ด๋ ๊ฒ ํด๋น ๋ฃจํ๋ ์กฐ๊ฑด์ ๋ง๋ ์์๋ง ์ ์งํ๋ฉด์ ๋ฐฐ์ด์ ์์ถ์ํจ๋ค.
- ํฌ ํฌ์ธํฐ(Two Pointers) ์๊ณ ๋ฆฌ์ฆ ํ์ฉ: r(์ฝ๊ธฐ)์ w(์ฐ๊ธฐ) ์ธ๋ฑ์ค
- ํ ๋ฒ์ ์ํ๋ก ๋ชจ๋ ์์ ์๋ฃ (๋ถํ์ํ ๋ฐ๋ณต ์ ๊ฑฐ)
3. ์์ ์ฌ๋ฐฐ์น
finally {
modCount += end - w;
shiftTailOverGap(es, w, end);
}
- modCount: ๊ตฌ์กฐ์ ๋ณ๊ฒฝ ํ์๋ฅผ ์ฆ๊ฐ์์ผ fail-fast ๋์์ ์ง์
- shiftTailOverGap: ์ ๊ฑฐ๋ ์์๋ค๋ก ์ธํ ๊ณต๋ฐฑ์ ์์ ๋ ์์
/** Erases the gap from lo to hi, by sliding down following elements. */
private void shiftTailOverGap(Object[] es, int lo, int hi) {
System.arraycopy(es, hi, es, lo, size - hi);
for (int to = size, i = (size -= hi - lo); i < to; i++)
es[i] = null;
}
1) ์ ๊ฑฐ ๊ตฌ๊ฐ ์ดํ์ ์์๋ค์ ์ ๊ฑฐ ๊ตฌ๊ฐ์ ์์ ์์น๋ก ๋ณต์ฌ
System.arraycopy(es, hi, es, lo, size - hi);
- es: ๋ด๋ถ ๋ฐฐ์ด
- hi: ์ ๊ฑฐ ๊ตฌ๊ฐ์ ๋ ์ธ๋ฑ์ค
- lo: ์ ๊ฑฐ ๊ตฌ๊ฐ์ ์์ ์ธ๋ฑ์ค
- size - hi: ์ ๊ฑฐ ๊ตฌ๊ฐ ์ดํ ๋จ์ ์์์ ๊ฐ์
2. ์ ํฌ๊ธฐ(i) ์ดํ๋ถํฐ ์๋ ํฌ๊ธฐ(to) ์ฌ์ด์ ๋ชจ๋ ์์๋ฅผ null๋ก ์ค์
for(int to= size, i =(size -= hi - lo); i <to; i++) es[i]=null;
- size -= hi - lo: ArrayList์ ํฌ๊ธฐ๋ฅผ ๊ฐฑ์ ํ๊ณ ์ ํฌ๊ธฐ๋ฅผ i์ ํ ๋น
- to: ์๋ ํฌ๊ธฐ
์์ฒ๋ผ ๋ฐฐ์ด์ด ์ฐธ์กฐํ ํ์ํ ํ์๊ฐ ์์ด์ง ๊ฒฝ์ฐ๋ null๋ก ์ค์ ํด ๊ฐ๋น์ง ์ปฌ๋ ์ ์ด ๊ฐ๋ฅํ๊ฒ ํด์ค๋ค.
์๊ฐ ๋ณต์ก๋
- Big O: ๊ฐ ์์๋ง๋ค c.contains() ํธ์ถ ํ์ (m : ์ปฌ๋ ์
c์ ํฌ๊ธฐ, n : ์ปฌ๋ ์
c์ contains ์๊ฐ ๋ณต์ก๋)
- List ํฌ๊ธฐ๊ฐ m์ด๋ฉด ์ต๋ m๋ฒ์ contains ํธ์ถ
- ArrayList/ LinkedList: contains๋ O(n), ์ด O(n·m)
- TreeSet : contains: O(log n), ์ด O(m* log n)
- HashSet: contains: O(1), ์ด O(n)
- ์ปฌ๋ ์
ํฌ๊ธฐ(m)๊ฐ ์์์ธ ๊ฒฝ์ฐ
- ์์: ์ปฌ๋ ์ c๊ฐ (m:100๊ฐ) ์ดํ์ ๊ณ ์ ๋ ํฌ๊ธฐ๋ฅผ ๊ฐ์ง ๋
- ๋ถ์
- c๊ฐ ArrayList์ธ ๊ฒฝ์ฐ: c.contains() ์๊ฐ ๋ณต์ก๋๋ O(n), ์ ์ฒด ์ฐ์ฐ: O(n × m) = O(n × 100) = O(n)
- c๊ฐ HashSet์ธ ๊ฒฝ์ฐ: c.contains() ์๊ฐ ๋ณต์ก๋๊ฐ O(1), ์ ์ฒด ์ฐ์ฐ: O(m) = O(1)
- ๊ฒฐ๋ก : m์ด ์์๋ผ๋ฉด ์ ํ ์๊ฐ(O(n)) ์ผ๋ก ์ฒ๋ฆฌ
- ์ปฌ๋ ์
ํฌ๊ธฐ(m)๊ฐ n์ ๋น๋กํ๋ ๊ฒฝ์ฐ
- ์์: c๊ฐ n์ 1% ํฌ๊ธฐ๋ฅผ ๊ฐ์ง ๋ (m = 0.01n)
- ๋ถ์:
- c๊ฐ ArrayList์ธ ๊ฒฝ์ฐ: c.contains() ์๊ฐ๋ณต์ก๋ O(m) = O(0.01n) ์ด๋ฏ๋ก, ์ ์ฒด ์ฐ์ฐ์ O(n × 0.01n) = O(n²)
- c๊ฐ HashSet์ธ ๊ฒฝ์ฐ: c.contains() ์๊ฐ๋ณต์ก๋ O(1), ์ ์ฒด ์ฐ์ฐ: O(n)
- ๊ฒฐ๋ก : m์ด n์ ๋น๋กํ๋ฉด O(n²)์ด ๋จ (๋จ, c๊ฐ HashSet์ O(n))
์ฐธ๊ณ ์๋ฃ :
์๋ฐ๋ก ๋ฐฐ์ฐ๋ ํต์ฌ ์๋ฃ๊ตฌ์กฐ์ ์๊ณ ๋ฆฌ์ฆ(Think Data Structures)
https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ArrayList.java#L897