๋ฌธ์ œ ํ•ด๊ฒฐ (PS)/์ž๋ฃŒ๊ตฌ์กฐ

[Java] removeAll, retainAll ๋‚ด๋ถ€ ๋™์ž‘ ๋ถ„์„, batchRemove

9taetae9 2025. 3. 3. 14:40
728x90

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๋กœ ์„ค์ •ํ•ด ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.


์‹œ๊ฐ„ ๋ณต์žก๋„

  1. 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)
  2. ์ปฌ๋ ‰์…˜ ํฌ๊ธฐ(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)) ์œผ๋กœ ์ฒ˜๋ฆฌ
  3. ์ปฌ๋ ‰์…˜ ํฌ๊ธฐ(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

728x90