F-lab Java 1์ฃผ์ฐจ / Phase 6 / Unit 6.3 ๋ณธ๊ฒฉ ํ์ต ์๋ฃ
9-์น์ ๋ง์คํฐ ํ๋กฌํํธ ํ์์ผ๋ก ๊น์ด ํํค์น๋ค.์ ์ ์ง์: Phase 4-5 (๋ฉ๋ชจ๋ฆฌ, GC), Unit 6.1-6.2 (String, StringBuilder)
๋ค์ Unit: 6.4 โ HashMap ๋ด๋ถ ๊ตฌ์กฐ โ โ โ์ด Unit์ ์๋ฏธ: ์๋ฐ ์ปฌ๋ ์ ์ ์์์ .
๋ฉด์ ๋จ๊ณจ ("์ธ์ ArrayList? ์ธ์ LinkedList?") + ์ค๋ฌด (CPU ์บ์ ์ง์ค).
Big-O ๋ง์ผ๋ก ๊ฒฐ์ ํ๋ฉด ํ๋ฆฐ๋ค โ ์ด Unit ์ ํต์ฌ ๋ฉ์์ง.
๋ ์๋ฃ๊ตฌ์กฐ์ ์ฐจ์ด๋ฅผ ์ผ์ ๋น์ ๋ก ํ์ด๋ณด๊ฒ ์ต๋๋ค.
[101๋] [102๋] [103๋] [104๋] [105๋] [106๋] [107๋]
โ โ
์์ ๋
ํน์ง:
ํต์ฌ: ์ธ๋ฑ์ค ์ง์ ์ ๊ทผ ๋น ๋ฆ, ์ค๊ฐ ์ฝ์ /์ญ์ ๋๋ฆผ.
[A ๋ณด๋ฌผ์์]
โ "๋ค์ ์์น๋ X ์ขํ"
[B ๋ณด๋ฌผ์์]
โ "๋ค์ ์์น๋ Y ์ขํ"
[C ๋ณด๋ฌผ์์]
โ "๋ค์ ์์น๋ Z ์ขํ"
[D ๋ณด๋ฌผ์์]
โ "์ฌ๊ธฐ๊ฐ ๋ง์ง๋ง"
ํน์ง:
ํต์ฌ: ์์ฐจ ํ์ ๋๋ฆผ, ์ค๊ฐ ์ฝ์ /์ญ์ ๋น ๋ฆ.
"๋ ์๋ฃ๊ตฌ์กฐ๋ Big-O ๊ฐ ๋ค๋ฅด๋ค โ ๊ทธ๋ฌ๋ ์ค์ ์ฑ๋ฅ์ CPU ์บ์ ์นํ๋๊ฐ ๊ฒฐ์ ํ๋ค."
๋น์ ์ ๋ฆฌ:
| ๋น์ | ์๋ฃ๊ตฌ์กฐ | ๊ฐ์ | ์ฝ์ |
|---|---|---|---|
| ์ํํธ ๋จ์ง | ArrayList | ์ธ๋ฑ์ค ์ ๊ทผ (O(1)) | ์ค๊ฐ ์ฝ์ (O(n)) |
| ๋ณด๋ฌผ์ฐพ๊ธฐ ์ฌ์ฌ | LinkedList | ์ค๊ฐ ์ฝ์ (O(1)) | ์ธ๋ฑ์ค ์ ๊ทผ (O(n)) |
โ ๊ทธ๋ฐ๋ฐ ํ๋ CPU ์์๋ ArrayList ๊ฐ ๊ฑฐ์ ํญ์ ๋น ๋ฆ (์ด์ ๋ ยง5์์).
ArrayList = ์ฑ ์ฅ์ ์ฑ ๋ค:
LinkedList = ํฉ์ด์ง ์ฑ ๋ค (ํธ์ง๋ก ์ฐ๊ฒฐ):
๋ฐฐ์ด๊ณผ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ CS ์ ๊ฐ์ฅ ๊ธฐ์ด์ ์ธ ์๋ฃ๊ตฌ์กฐ.
array[i] = base_address + i * size๋ฉ๋ชจ๋ฆฌ ์ฃผ์: 1000 1004 1008 1012 1016
๋ฐ์ดํฐ: [10] [20] [30] [40] [50]
์ธ๋ฑ์ค: 0 1 2 3 4
array[3] โ 1000 + 3*4 = 1012 โ ์ฆ์ ์ ๊ทผ
๋ฌธ์ :
์ฃผ์ 1000 ์ฃผ์ 5000 ์ฃผ์ 3000
[10][next=5000] โ [20][next=3000] โ [30][next=null]
์ฅ์ :
๋จ์ :
์๋ฐ 1.2 (1998) ์ ์ปฌ๋ ์ ํ๋ ์์ํฌ ๋ ๋ ๊ฐ์ง ๋ชจ๋ ์ ๊ณต:
public interface List<E> extends Collection<E> {
E get(int index);
void add(E element);
void add(int index, E element);
E remove(int index);
// ...
}
public class ArrayList<E> implements List<E> {
transient Object[] elementData; // ๋ด๋ถ ๋ฐฐ์ด
private int size;
}
public class LinkedList<E> implements List<E> {
transient Node<E> first; // ์ฒซ ๋
ธ๋
transient Node<E> last; // ๋ง์ง๋ง ๋
ธ๋
transient int size;
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
}
}
โ ๊ฐ์ List ์ธํฐํ์ด์ค, ์์ ํ ๋ค๋ฅธ ๋ด๋ถ ๊ตฌ์กฐ.
| ์ฐ์ฐ | ArrayList | LinkedList |
|---|---|---|
get(i) | O(1) โญ | O(n) |
add(e) (๋) | O(1) amortized | O(1) โญ |
add(0, e) (์) | O(n) | O(1) โญ |
add(i, e) (์ค๊ฐ) | O(n) | O(n) ํ์ + O(1) ์ฝ์ |
remove(i) | O(n) | O(n) (ํ์์ด ๋น์) |
contains(e) | O(n) | O(n) |
size() | O(1) | O(1) |
Big-O ๋ง ๋ณด๋ฉด:
"์ด๋ก ๊ณผ ์ค๋ฌด์ ๊ดด๋ฆฌ โ CPU ์บ์ ์นํ๋."
LinkedList ์ ๋ชจ๋ ๋ ธ๋๋ Heap ์ ํฉ์ด์ง ์์น:
CPU ์บ์ ๋ผ์ธ (๋ณดํต 64 ๋ฐ์ดํธ):
๊ฒฐ๊ณผ:
โ ์์ฐจ ์ฒ๋ฆฌ ์ LinkedList ๊ฐ 100๋ฐฐ ๋๋ฆด ์ ์์.
"Big-O ๋ ์์์ด์ง ๋์ด ์๋๋ค."
์๋ฃ๊ตฌ์กฐ ์ ํ์ ์๊ณ ๋ฆฌ์ฆ ๋ณต์ก๋ + ์ค์ ๋ฉ๋ชจ๋ฆฌ ๋์ + ์ฌ์ฉ ํจํด ์ ์ข ํฉ. ArrayList ๋ ๊ฑฐ์ ๋ชจ๋ ์ค๋ฌด์์ ์ ๋ต. LinkedList ๋ ๋งค์ฐ ํน์ ํ ์ํฉ์์๋ง ์๋ฏธ. ์ด ์ฌ์ค์ ์๋ ๊ฒ์ด ์๋์ด ์ฐจ๋ณํ ํฌ์ธํธ.
@Service
public class CargoService {
public List<Cargo> processCargos(BookingRequest request) {
// โ "์ฝ์
๋ง์ผ๋๊น LinkedList!" โ ํํ ์คํด
List<Cargo> cargos = new LinkedList<>();
// 1๋ง ๊ฐ ํ๋ฌผ ์ถ๊ฐ
for (CargoData data : request.getCargoDataList()) {
cargos.add(parseCargo(data)); // ๋์ ์ถ๊ฐ
}
// ์ฒ๋ฆฌ
for (Cargo cargo : cargos) {
processCargo(cargo); // ์์ฐจ ์ฒ๋ฆฌ
}
return cargos;
}
}
๋ฌธ์ :
ํด๊ฒฐ:
List<Cargo> cargos = new ArrayList<>(); // ๊ฑฐ์ ํญ์ ์ ๋ต
Q: "ArrayList ์ LinkedList ์ ์ฐจ์ด๋?"
A: "ArrayList ๋ ๋ฐฐ์ด ๊ธฐ๋ฐ, LinkedList ๋ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๊ธฐ๋ฐ์ด์์" โญ ๊ธฐ์ด
์๋์ด ๋ต๋ณ:
// โ LinkedList ์ ์ธ๋ฑ์ค ๋ฐ๋ณต ์ ๊ทผ
List<Booking> bookings = new LinkedList<>(loadBookings());
for (int i = 0; i < bookings.size(); i++) {
Booking b = bookings.get(i); // โ ๏ธ ๋งค๋ฒ O(n) ํ์!
process(b);
}
// ์๊ฐ ๋ณต์ก๋: O(nยฒ)
// 1๋ง ๊ฐ โ 1์ต ๋ฒ ๋น๊ต
๋ฌธ์ :
get(i) ๊ฐ LinkedList ์์๋ O(n) ํ์ํด๊ฒฐ 1: ArrayList
List<Booking> bookings = new ArrayList<>(loadBookings());
for (int i = 0; i < bookings.size(); i++) {
Booking b = bookings.get(i); // O(1)
}
// O(n) ์๊ฐ ๋ณต์ก๋
ํด๊ฒฐ 2: Iterator (LinkedList ๋ผ๋)
List<Booking> bookings = ...; // ์ด๋ค List ๋
for (Booking b : bookings) { // ํฅ์๋ for = Iterator
process(b);
}
// LinkedList ์์๋ O(n)
// 100๋ง ๊ฐ Integer ์ ์ฅ
List<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < 1_000_000; i++) {
arrayList.add(i);
linkedList.add(i);
}
๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ธก์ :
์ 3๋ฐฐ?:
๊ฒฐ๊ณผ:
// โ ์ํ ์ค ์์
List<Cargo> cargos = new ArrayList<>(allCargos);
for (Cargo c : cargos) {
if (c.isExpired()) {
cargos.remove(c); // โ ๏ธ ConcurrentModificationException!
}
}
๋ฌธ์ :
modCount ๋ฅผ ์ถ์ ํด๊ฒฐ 1: Iterator.remove()
Iterator<Cargo> iter = cargos.iterator();
while (iter.hasNext()) {
if (iter.next().isExpired()) {
iter.remove(); // โ ์์
}
}
ํด๊ฒฐ 2: Java 8 removeIf
cargos.removeIf(Cargo::isExpired); // โ ๊ฐ์ฅ ๊น๋
ํด๊ฒฐ 3: Stream
cargos = cargos.stream()
.filter(c -> !c.isExpired())
.collect(Collectors.toList());
// ์๋๋ฆฌ์ค: ํ๋ฌผ 100๋ง ๊ฑด์ ์์น ์
๋ฐ์ดํธ
public class CargoTrackingService {
private List<CargoLocation> locations; // ์ด๋ค List?
public void updateLocation(int cargoId, Coordinate coord) {
CargoLocation loc = locations.get(cargoId); // O(?)
loc.setCoordinate(coord);
}
public void addCargo(CargoLocation loc) {
locations.add(loc); // O(?)
}
public List<CargoLocation> getCargosNearby(Coordinate target) {
// ์์ฐจ ํ์
return locations.stream()
.filter(l -> distance(l, target) < 100)
.collect(Collectors.toList());
}
}
ArrayList ์ ํ ์:
get(cargoId): O(1) โญ (์์ฃผ ํธ์ถ๋๋ ์์น ์
๋ฐ์ดํธ)add(): O(1) amortizedLinkedList ์ ํ ์:
get(cargoId): O(n) โ 100๋ง ๋ฒ ํ์! โ ๏ธโ ArrayList ์๋์ ์น.
| ์๋๋ฆฌ์ค | LinkedList ์๋ชป ์ ํ | ArrayList ์ ๋ต |
|---|---|---|
| ํ๋ฌผ ๋ชฉ๋ก ์์ฐจ ์ฒ๋ฆฌ | 5~10๋ฐฐ ๋๋ฆผ | ๋น ๋ฆ |
| ์ธ๋ฑ์ค ์ ๊ทผ ๋ฐ๋ณต | O(nยฒ) | O(n) |
| ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ | 3๋ฐฐ ์ฌ์ฉ | ํจ์จ |
| ์์น ์ ๋ฐ์ดํธ | ์ฌ์ค์ ๋ฉ์ถค | ์ ์ |
| ๋ฉด์ ๋ต๋ณ | ํ๋ฉด์ | ์๋์ด |
โ ์๋ฃ๊ตฌ์กฐ ์ ํ์ ์๋์ด ์๋ฐ ๊ฐ๋ฐ์์ ํต์ฌ ์ญ๋.
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
System.out.println(list.get(1)); // "Banana"
System.out.println(list.size()); // 3
// ์ธ๋ฑ์ค ๊ธฐ๋ฐ ๋ฐ๋ณต
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// ํฅ์๋ for (๊ถ์ฅ)
for (String fruit : list) {
System.out.println(fruit);
}
ArrayList<String> list = new ArrayList<>();
// === ์ถ๊ฐ ===
list.add("A"); // O(1) amortized
list.add(0, "Front"); // O(n) โ ๋ชจ๋ ์์ ์ด๋
list.addAll(otherList); // O(m)
// === ์กฐํ ===
String s = list.get(0); // O(1) โญ
int idx = list.indexOf("A"); // O(n)
boolean has = list.contains("A"); // O(n)
// === ์์ ===
list.set(0, "X"); // O(1)
// === ์ญ์ ===
list.remove(0); // O(n) โ ๋ค ์์ ์ด๋
list.remove("A"); // O(n)
list.removeIf(s -> s.startsWith("X")); // O(n)
// === ์ ๋ณด ===
list.size(); // O(1)
list.isEmpty(); // O(1)
list.clear(); // O(n)
// === ๋ณํ ===
list.toArray(); // O(n)
list.subList(0, 2); // O(1) view
// โ ๊ธฐ๋ณธ capacity (10)
List<Cargo> cargos = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
cargos.add(...); // ์ฝ 14๋ฒ ํ์ฅ ๋ฐ์
}
// โ ๋ฏธ๋ฆฌ ์ค์
List<Cargo> cargos = new ArrayList<>(100000);
for (int i = 0; i < 100000; i++) {
cargos.add(...); // ํ์ฅ ์์
}
ํจ๊ณผ: 30~50% ์ฑ๋ฅ ํฅ์ (ํฐ ๋ฐ์ดํฐ์ผ์๋ก ํผ).
LinkedList<String> list = new LinkedList<>();
list.add("A");
list.addFirst("Front"); // โ
์์ ์ถ๊ฐ
list.addLast("Last"); // โ
๋์ ์ถ๊ฐ
// LinkedList ๋ง์ ๋ฉ์๋
list.peekFirst(); // "Front"
list.peekLast(); // "Last"
list.pollFirst(); // "Front" ์ ๊ฑฐ + ๋ฐํ
list.pollLast(); // "Last" ์ ๊ฑฐ + ๋ฐํ
// LinkedList ๋ Deque ์ธํฐํ์ด์ค๋ ๊ตฌํ
Deque<String> deque = new LinkedList<>();
// ์คํ์ฒ๋ผ ์ฌ์ฉ
deque.push("A");
deque.push("B");
deque.pop(); // "B"
// ํ์ฒ๋ผ ์ฌ์ฉ
deque.offer("A");
deque.offer("B");
deque.poll(); // "A"
๊ทธ๋ฌ๋: Deque ๊ฐ ํ์ํ๋ฉด ArrayDeque ๊ฐ ๋ ์ข์ (์บ์ ์นํ).
Case 1: ์ ๋์์๋ง ๋น๋ฒํ ์ฝ์ /์ญ์ + ์์ฐจ ์ ๊ทผ
// ์์
ํ (FIFO) โ ๊ทธ๋ฌ๋ ArrayDeque ๊ฐ ๋ ์ข์
LinkedList<Task> queue = new LinkedList<>();
queue.offer(task); // ๋์ ์ถ๊ฐ
queue.poll(); // ์์์ ์ ๊ฑฐ
Case 2: ์ฝ์ /์ญ์ ๋ง ๋งค์ฐ ๋น๋ฒ + ์ธ๋ฑ์ค ์ ๊ทผ X
// ๊ทธ๋ฌ๋ ์ค๋ฌด์์ ๊ฑฐ์ ์์
โ ํ์ค์ ์ผ๋ก LinkedList ์ฌ์ฉ์ฒ๋ ๊ฑฐ์ ์์. ArrayList ๋๋ ArrayDeque ๊ฐ ํญ์ ๋ ์ข์.
| ํญ๋ชฉ | ArrayList | LinkedList |
|---|---|---|
| ๋ด๋ถ ๊ตฌ์กฐ | ๋์ ๋ฐฐ์ด | ์ด์ค ์ฐ๊ฒฐ ๋ฆฌ์คํธ |
| ์ธ๋ฑ์ค ์ ๊ทผ | O(1) โญ | O(n) |
| ๋ ์ถ๊ฐ | O(1) amortized | O(1) |
| ์ ์ถ๊ฐ | O(n) | O(1) |
| ์ค๊ฐ ์ถ๊ฐ (ํ์ ํ) | O(n) | O(n) |
| ๋ฉ๋ชจ๋ฆฌ/์์ | ~8 ๋ฐ์ดํธ | ~40 ๋ฐ์ดํธ (5๋ฐฐ) |
| ์บ์ ์นํ๋ | ๋์ โญ | ๋ฎ์ |
| ์์ฐจ ๋ฐ๋ณต | ๋น ๋ฆ โญ | ๋๋ฆผ (5~10๋ฐฐ) |
| ๊ถ์ฅ๋ | ๊ฑฐ์ ๋ชจ๋ ๊ฒฝ์ฐ | ๋งค์ฐ ํน์ํ ๊ฒฝ์ฐ |
List ๊ฐ ํ์ํ๋ค โ ArrayList โญ (90% ์ด์์ ๊ฒฝ์ฐ)
โ
์ ๋ง ์ ๋์์๋ง ๋น๋ฒํ ์ฝ์
?
โโโ YES โ ArrayDeque โญ (LinkedList ๋ณด๋ค ์ข์)
โโโ NO โ ArrayList
๊ฒฐ๋ก : ์๋ฐ์์ LinkedList ๋ ๊ฑฐ์ ์ฌ์ฉํ์ง ์์.
// ์์ฃผ ์ฐ๋ ํจํด
List<Cargo> result = cargos.stream()
.filter(c -> c.getWeight() > 100)
.collect(Collectors.toList()); // ๊ธฐ๋ณธ ArrayList
// Java 16+ ์ toList()
List<Cargo> result = cargos.stream()
.filter(c -> c.getWeight() > 100)
.toList(); // ๋ถ๋ณ List
Collectors.toList() ์ ๊ฒฐ๊ณผ: ๊ธฐ๋ณธ์ ์ผ๋ก ArrayList.
// Java 9+
List<String> immutable = List.of("A", "B", "C");
// ์๋ ์ UnsupportedOperationException
immutable.add("D"); // โ ์์ธ
์ฉ๋: ์์ ๋ฆฌ์คํธ, ๋ถ๋ณ์ฑ ๋ณด์ฅ ํ์ ์.
public class ArrayList<E> {
transient Object[] elementData; // ๋ด๋ถ ๋ฐฐ์ด
private int size; // ์ค์ ์ฌ์ฉ ์ค ๊ฐ์
// ๊ธฐ๋ณธ capacity
private static final int DEFAULT_CAPACITY = 10;
}
ํต์ฌ:
elementData.length = capacity (๋ฐฐ์ด ํฌ๊ธฐ)size = ํ์ฌ ์ฌ์ฉ ๊ฐ์size โค capacitypublic boolean add(E e) {
modCount++;
if (size == elementData.length) {
elementData = grow(); // ํ์ฅ!
}
elementData[size] = e;
size++;
return true;
}
private Object[] grow() {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5๋ฐฐ
return Arrays.copyOf(elementData, newCapacity);
}
ํ์ฅ ๊ณต์: newCapacity = oldCapacity * 1.5
์ด๊ธฐ: 10
11๋ฒ์งธ ์ถ๊ฐ: 10 * 1.5 = 15
16๋ฒ์งธ ์ถ๊ฐ: 15 * 1.5 = 22
23๋ฒ์งธ ์ถ๊ฐ: 22 * 1.5 = 33
...
โ ์ ์ง์ ์ผ๋ก ํฐ ํญ ํ์ฅ, amortized O(1).
public E get(int index) {
Objects.checkIndex(index, size);
return (E) elementData[index]; // ๋ฐฐ์ด ์ง์ ์ ๊ทผ
}
O(1): ๋ฉ๋ชจ๋ฆฌ ์ฃผ์ ์ง์ ๊ณ์ฐ.
public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
if (size == elementData.length) {
elementData = grow();
}
// ๋ค ์์๋ฅผ ํ ์นธ์ฉ ๋ค๋ก ์ด๋
System.arraycopy(elementData, index,
elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
O(n): arraycopy ๊ฐ ๋น ๋ฅด๊ธด ํ์ง๋ง n ์ ๋น๋ก.
elementData = [A][B][C][D][E][_][_][_][_][_]
โ โ โ
0 size=5 capacity=10
JVM Heap:
[ArrayList ๊ฐ์ฒด]
size: 5
elementData: โโโโโโโโโโโโโ
โ
[Object[] ๋ฐฐ์ด โ ์ฐ์ ๋ฉ๋ชจ๋ฆฌ]
[A][B][C][D][E][null][null][null][null][null]
ํต์ฌ: ๋ฐฐ์ด์ ํ๋์ ์ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ๋ธ๋ก.
public class LinkedList<E> {
transient int size = 0;
transient Node<E> first; // ์ฒซ ๋
ธ๋
transient Node<E> last; // ๋ง์ง๋ง ๋
ธ๋
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
}
ํต์ฌ:
Node ๊ฐ์ฒด๋ก ๊ฐ์ธ์งprev + next ํฌ์ธํฐ๋ก ์๋ฐฉํฅ ์ฐ๊ฒฐJVM Heap:
[LinkedList ๊ฐ์ฒด]
size: 5
first: โโโ
last: โโโผโโ
โ โ
[Node A] โ โ
prev: null
item: "A"
next: โโโ[Node B]
โ โ prev: โโโ[Node A]
โ โ item: "B"
โ โ next: โโโ[Node C]
โ โ
โ โ
โ โ [Node E]
โ โโโโ prev: โโโ[Node D]
item: "E"
next: null
ํต์ฌ:
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
if (index < (size >> 1)) {
// ์์ชฝ์ด๋ฉด first ๋ถํฐ
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next; // โ
์์ฐจ ํ์
return x;
} else {
// ๋ค์ชฝ์ด๋ฉด last ๋ถํฐ
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev; // โ
์์ฐจ ํ์
return x;
}
}
O(n/2) = O(n): ๊ทธ๋ฌ๋ ์๋ฐฉํฅ ํ์์ผ๋ก ์ ๋ฐ์ ์ ์ฝ.
โ ์ธ๋ฑ์ค ์ ๊ทผ์ LinkedList ์ฌ์ฉ ์ ๋ X.
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null); // โ
Node ๊ฐ์ฒด ์์ฑ!
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
}
O(1): ๋ง์ง๋ง ๋ ธ๋์ ์ง์ ์ฐ๊ฒฐ.
๊ทธ๋ฌ๋:
[CPU Core] โโโ 1 ns
โ
[L1 Cache] (์์ญ KB)
โ โโ 5 ns
[L2 Cache] (์๋ฐฑ KB)
โ โโ 15 ns
[L3 Cache] (์ MB)
โ โโ 30 ns
[Main Memory (RAM)] โโ 100 ns
โ
[Disk] โโ 10ms
์บ์ ๋ผ์ธ (cache line):
List<Integer> list = new ArrayList<>(...);
for (Integer i : list) {
// i ์ฒ๋ฆฌ
}
๋ฉ๋ชจ๋ฆฌ ๊ทธ๋ฆผ:
๋ฐฐ์ด (์ฐ์):
[I0][I1][I2][I3][I4][I5][I6][I7]...
โโโโ 64 ๋ฐ์ดํธ ์บ์ ๋ผ์ธ โโโโโ
L1 ์บ์์ ํ ๋ฒ ๋ก๋ โ 8๊ฐ ์ฒ๋ฆฌ ๊ฐ๋ฅ (4 ๋ฐ์ดํธ int ๊ธฐ์ค)
โ ํ๊ท 1 ns per ์์
์ด๋ก : 1๋ง ๊ฐ ์ฒ๋ฆฌ ์ ์ฝ 0.01 ms.
List<Integer> list = new LinkedList<>(...);
for (Integer i : list) {
// i ์ฒ๋ฆฌ
}
๋ฉ๋ชจ๋ฆฌ ๊ทธ๋ฆผ:
Node A (์ฃผ์ 1000)
โ Node B (์ฃผ์ 7300) โ ์บ์ ๋ผ์ธ ๋ค๋ฆ
โ Node C (์ฃผ์ 4200) โ ๋ ๋ค๋ฆ
โ Node D (์ฃผ์ 12000) โ ๋ ๋ค๋ฆ
๋งค ์์๋ง๋ค:
ํ์ค: 1๋ง ๊ฐ ์ฒ๋ฆฌ ์ ์ฝ 0.5 ms (50๋ฐฐ ๋๋ฆผ).
public class CacheTest {
public static void main(String[] args) {
int N = 10_000_000;
List<Integer> arr = new ArrayList<>(N);
List<Integer> linked = new LinkedList<>();
for (int i = 0; i < N; i++) {
arr.add(i);
linked.add(i);
}
// ArrayList ์ํ
long start = System.nanoTime();
long sum1 = 0;
for (Integer i : arr) sum1 += i;
long arrTime = System.nanoTime() - start;
// LinkedList ์ํ
start = System.nanoTime();
long sum2 = 0;
for (Integer i : linked) sum2 += i;
long linkedTime = System.nanoTime() - start;
System.out.println("ArrayList: " + arrTime / 1_000_000 + " ms");
// ์ฝ 30 ms
System.out.println("LinkedList: " + linkedTime / 1_000_000 + " ms");
// ์ฝ 200 ms (7๋ฐฐ ๋๋ฆผ)
}
}
๊ฒฐ๊ณผ: 5~10๋ฐฐ ์ฐจ์ด (๋ฉ๋ชจ๋ฆฌ ๋จํธํ ์ ๋์ ๋ฐ๋ผ 30๋ฐฐ๊น์ง).
Big-O ์ ๊ฐ์ :
ํ๋ CPU ์ ์ง์ค:
public class ArrayList<E> {
protected transient int modCount = 0;
public boolean add(E e) {
modCount++; // ์์ ์ ์ฆ๊ฐ
// ...
}
private class Itr implements Iterator<E> {
int expectedModCount = modCount;
public E next() {
checkForComodification();
// ...
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
Fail-Fast ์์น:
@Service
public class CargoService {
public List<Cargo> getCargosByBooking(Long bookingId) {
// โ ArrayList โ ํ์ค ์ ํ
List<Cargo> cargos = new ArrayList<>();
// DB ์์ ํ๋ฌผ ๋ก๋
try (PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM cargos WHERE booking_id = ?")) {
ps.setLong(1, bookingId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
cargos.add(mapToCargo(rs)); // O(1)
}
}
}
return cargos;
}
public Cargo getCargoByIndex(List<Cargo> cargos, int index) {
return cargos.get(index); // O(1) โญ ArrayList ์ ๊ฐ์
}
public List<Cargo> filterHeavy(List<Cargo> cargos, double minWeight) {
// ์์ฐจ ์ฒ๋ฆฌ โ ArrayList ์ ์บ์ ์นํ๋ ํ์ฉ
return cargos.stream()
.filter(c -> c.getWeight() >= minWeight)
.collect(Collectors.toList());
}
}
@Service
public class BookingReportService {
public List<BookingItem> generateMonthlyReport(int year, int month) {
int estimatedSize = bookingRepository.countByMonth(year, month);
// โ ๋ฏธ๋ฆฌ capacity ์ค์
List<BookingItem> items = new ArrayList<>(estimatedSize);
bookingRepository.streamByMonth(year, month)
.forEach(b -> items.add(toBookingItem(b)));
return items;
}
}
ํจ๊ณผ: ํฐ ๋ฐ์ดํฐ์์ 30~50% ์ฑ๋ฅ ํฅ์.
public class CargoFilterService {
// โ ์ํ
public void removeExpiredUnsafe(List<Cargo> cargos) {
for (Cargo c : cargos) {
if (c.isExpired()) {
cargos.remove(c); // ConcurrentModificationException!
}
}
}
// โ Iterator
public void removeExpiredIterator(List<Cargo> cargos) {
Iterator<Cargo> iter = cargos.iterator();
while (iter.hasNext()) {
if (iter.next().isExpired()) {
iter.remove(); // ์์
}
}
}
// โ removeIf (Java 8+)
public void removeExpiredModern(List<Cargo> cargos) {
cargos.removeIf(Cargo::isExpired); // ๊ฐ์ฅ ๊น๋
}
// โ Stream (์ List ์์ฑ)
public List<Cargo> filterAlive(List<Cargo> cargos) {
return cargos.stream()
.filter(c -> !c.isExpired())
.collect(Collectors.toList());
}
}
@Service
public class TaskQueueService {
// โ LinkedList ๋ก ํ ๊ตฌํ
private LinkedList<Task> queue = new LinkedList<>();
// โ ArrayDeque โ ๋ ๋น ๋ฆ!
private ArrayDeque<Task> queue = new ArrayDeque<>();
public void enqueue(Task task) {
queue.offer(task);
}
public Task dequeue() {
return queue.poll();
}
}
์ ArrayDeque ๊ฐ ๋ ์ข์๊ฐ?:
@Service
public class CargoSearchService {
// โ ArrayList โ ์ธ๋ฑ์ค ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์
public List<Cargo> getPage(int page, int pageSize) {
List<Cargo> all = loadAll(); // ArrayList
int from = page * pageSize;
int to = Math.min(from + pageSize, all.size());
// O(1) โ subList ๋ view
return all.subList(from, to);
}
}
LinkedList ๋ผ๋ฉด:
subList ๋ ๋ด๋ถ์ ์ผ๋ก ์ธ๋ฑ์ค ํ์ โ O(n) ์ถ๊ฐ ๋น์ฉpublic class ListBenchmark {
public static void main(String[] args) {
int N = 1_000_000;
// 1. ๋์ ์ถ๊ฐ
long arrayListAdd = benchmark(() -> {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < N; i++) list.add(i);
});
long linkedListAdd = benchmark(() -> {
List<Integer> list = new LinkedList<>();
for (int i = 0; i < N; i++) list.add(i);
});
// 2. ์์ฐจ ์ํ
List<Integer> arr = new ArrayList<>(/* 1M items */);
List<Integer> lnk = new LinkedList<>(/* 1M items */);
long arrIter = benchmark(() -> {
long sum = 0;
for (Integer i : arr) sum += i;
});
long lnkIter = benchmark(() -> {
long sum = 0;
for (Integer i : lnk) sum += i;
});
// 3. ์ธ๋ฑ์ค ์ ๊ทผ
long arrGet = benchmark(() -> {
for (int i = 0; i < N; i++) arr.get(i);
});
long lnkGet = benchmark(() -> {
for (int i = 0; i < N; i++) lnk.get(i); // โ ๏ธ O(nยฒ)
});
System.out.println("Add (end):");
System.out.println(" ArrayList: " + arrayListAdd + "ms"); // ~30ms
System.out.println(" LinkedList: " + linkedListAdd + "ms"); // ~40ms
System.out.println("Iterate:");
System.out.println(" ArrayList: " + arrIter + "ms"); // ~5ms
System.out.println(" LinkedList: " + lnkIter + "ms"); // ~50ms (10๋ฐฐ)
System.out.println("Get(i):");
System.out.println(" ArrayList: " + arrGet + "ms"); // ~10ms
System.out.println(" LinkedList: " + lnkGet + "ms"); // ~์์ญ ๋ถ (์ฌ์ค์ ๋ฉ์ถค)
}
static long benchmark(Runnable task) {
long start = System.currentTimeMillis();
task.run();
return System.currentTimeMillis() - start;
}
}
public class CollectionConverter {
// ๋ฐฐ์ด โ ArrayList
Cargo[] array = ...;
List<Cargo> list1 = new ArrayList<>(Arrays.asList(array));
List<Cargo> list2 = Arrays.stream(array).collect(Collectors.toList());
// List โ ๋ฐฐ์ด
Cargo[] arr = list1.toArray(new Cargo[0]);
// List โ ๋ค๋ฅธ List
List<Cargo> immutable = List.copyOf(list1); // ๋ถ๋ณ
List<Cargo> sorted = list1.stream()
.sorted(Comparator.comparing(Cargo::getWeight))
.collect(Collectors.toList());
}
public class ThreadSafeListExamples {
// โ ์ผ๋ฐ ArrayList โ ๋์ ์์ ์ ๋ฐ์ดํฐ ์์
private List<Cargo> unsafeList = new ArrayList<>();
// โ 1. Collections.synchronizedList
private List<Cargo> synced = Collections.synchronizedList(new ArrayList<>());
// ๋ชจ๋ ๋ฉ์๋ synchronized โ ๊ทธ๋ฌ๋ ์ํ ์ ์ธ๋ถ ๋๊ธฐํ ํ์!
synchronized (synced) {
for (Cargo c : synced) { // ์ํ๋ ๋ช
์์ ๋๊ธฐํ
// ...
}
}
// โ 2. CopyOnWriteArrayList
private List<Cargo> cow = new CopyOnWriteArrayList<>();
// ์ฐ๊ธฐ ์ ์ ์ฒด ๋ณต์ฌ โ ์ฝ๊ธฐ ๋ง๊ณ ์ฐ๊ธฐ ์ ์ ๊ฒฝ์ฐ
// โ 3. ConcurrentLinkedQueue (List ์๋)
private Queue<Cargo> queue = new ConcurrentLinkedQueue<>();
// ์ง์ ํ ๋์์ฑ ํ
}
// โ ํํ ์คํด
List<Cargo> cargos = new LinkedList<>();
for (CargoData data : dataList) {
cargos.add(parse(data)); // ๋์ ์ถ๊ฐ
}
์ง์ค:
ํด๊ฒฐ: ArrayList ๊ฐ ํ์ค.
// โ ์น๋ช
์
LinkedList<Booking> list = ...;
for (int i = 0; i < list.size(); i++) {
process(list.get(i)); // O(nยฒ)!
}
// 1๋ง ๊ฐ โ 1์ต ๋ฒ ๋น๊ต โ ๋ถ ๋จ์
ํด๊ฒฐ: ํฅ์๋ for (Iterator ์ฌ์ฉ)
for (Booking b : list) { // O(n)
process(b);
}
// โ ๋นํจ์จ
List<Cargo> cargos = new ArrayList<>();
for (Cargo c : newCargos) {
cargos.add(0, c); // O(n) โ ๋ชจ๋ ์์ ์ด๋
}
// 1๋ง ๊ฐ โ O(nยฒ)
ํด๊ฒฐ 1: ๋์ ์ถ๊ฐ ํ reverse
for (Cargo c : newCargos) {
cargos.add(c);
}
Collections.reverse(cargos); // O(n)
ํด๊ฒฐ 2: ArrayDeque
Deque<Cargo> deque = new ArrayDeque<>();
for (Cargo c : newCargos) {
deque.offerFirst(c); // O(1)
}
// โ ์ํ ์ค ์์
for (Cargo c : cargos) {
if (c.isExpired()) {
cargos.remove(c); // ์์ธ!
}
}
ํด๊ฒฐ: removeIf
cargos.removeIf(Cargo::isExpired); // ๊ฐ์ฅ ๊น๋
List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4, 5));
List<Integer> sub = list.subList(1, 4); // [2, 3, 4]
// subList ๋ view!
sub.add(99);
// โ list ๋ ๋ณํจ: [1, 2, 3, 4, 99, 5]
// ์๋ณธ์ ์์ ํ๋ฉด?
list.add(0, 100);
sub.size(); // ConcurrentModificationException!
ํด๊ฒฐ: ์ List ์์ฑ
List<Integer> sub = new ArrayList<>(list.subList(1, 4)); // ๋ณต์ฌ
// โ ํฐ ๋ฐ์ดํฐ์ธ๋ฐ ๊ธฐ๋ณธ capacity (10)
List<Item> items = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
items.add(...); // ์ฝ 30๋ฒ ํ์ฅ ๋ฐ์
}
ํด๊ฒฐ: ๋ฏธ๋ฆฌ ์ค์
List<Item> items = new ArrayList<>(1_000_000);
List<String> list = List.of("A", "B", "C");
list.add("D"); // โ UnsupportedOperationException
// List.of() ๋ ๋ถ๋ณ!
ํด๊ฒฐ: ๊ฐ๋ณ์ด ํ์ํ๋ฉด
List<String> list = new ArrayList<>(List.of("A", "B", "C"));
list.add("D"); // โ
List<Cargo> list1 = new ArrayList<>(...);
List<Cargo> list2 = new LinkedList<>(...);
// ๋ ๋ฆฌ์คํธ๊ฐ ๊ฐ์ ์์๋ฉด?
list1.equals(list2); // true (๊ตฌํ ๋ค๋ฅด์ง๋ง ๋ด์ฉ ๊ฐ์ผ๋ฉด)
// HashMap ํค๋ก ์ฌ์ฉ ์?
Map<List<Cargo>, String> map = new HashMap<>();
map.put(list1, "value");
// ๊ทธ๋ฌ๋ List ๋ฅผ ์์ ํ๋ฉด?
list1.add(new Cargo(...));
map.get(list1); // null! (hashCode ๋ณ๊ฒฝ๋จ)
์์น: ๊ฐ๋ณ List ๋ฅผ HashMap ํค๋ก ์ฌ์ฉ X.
[Unit 6.1: String + Constant Pool] โ
โ
[Unit 6.2: StringBuilder vs StringBuffer] โ
โ
[Unit 6.3: ArrayList vs LinkedList] โ ์ง๊ธ ์ฌ๊ธฐ โ
โ
[Unit 6.4: HashMap ๋ด๋ถ ๊ตฌ์กฐ] โ
โ
โ
(๋ค์, ํต์ฌ)
โ
[Unit 6.5: TreeMap, LinkedHashMap]
| Phase | ์ฐ๊ฒฐ |
|---|---|
| 4.1 (JVM ๋ฉ๋ชจ๋ฆฌ) | ๋ฐฐ์ด์ Heap ์ ์ฐ์ ๋ฉ๋ชจ๋ฆฌ |
| 5.1 (GC) | LinkedList ์ Node ๋ค์ด GC ๋ถ๋ด |
| 5.2 (Heap ๊ตฌ์กฐ) | ํฐ List ๋ Old Gen ์ผ๋ก promotion |
Collection (์ธํฐํ์ด์ค)
โโโ List
โ โโโ ArrayList โญ
โ โโโ LinkedList
โ โโโ Vector (legacy, synchronized)
โ
โโโ Set
โ โโโ HashSet
โ โโโ LinkedHashSet
โ โโโ TreeSet
โ
โโโ Queue / Deque
โโโ ArrayDeque โญ
โโโ LinkedList (Deque ๋ ๊ตฌํ)
โโโ PriorityQueue
Map (๋ณ๋ ์ธํฐํ์ด์ค)
โโโ HashMap โญ (Unit 6.4)
โโโ LinkedHashMap (Unit 6.5)
โโโ TreeMap (Unit 6.5)
โโโ ConcurrentHashMap
| ์๋ฃ๊ตฌ์กฐ | get | add | remove | contains |
|---|---|---|---|---|
| ArrayList | O(1) | O(1)* | O(n) | O(n) |
| LinkedList | O(n) | O(1)* | O(n) | O(n) |
| HashSet | - | O(1) | O(1) | O(1) |
| TreeSet | - | O(log n) | O(log n) | O(log n) |
| HashMap | - | O(1) | O(1) | O(1) |
| TreeMap | - | O(log n) | O(log n) | O(log n) |
*amortized
| ์ธ์ด | ๋์ ๋ฐฐ์ด | ์ฐ๊ฒฐ ๋ฆฌ์คํธ |
|---|---|---|
| Java | ArrayList | LinkedList |
| C++ | std::vector | std::list |
| Python | list | (์์, deque) |
| JavaScript | Array | (์์) |
| C# | List<T> | LinkedList<T> |
| Rust | Vec<T> | LinkedList<T> |
โ ๊ฑฐ์ ๋ชจ๋ ์ธ์ด๊ฐ ๋์ ๋ฐฐ์ด ํ์ค + ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ ์ต์ .
| ์ง๋ฌธ | ์ด Unit ์์์ ๋ต |
|---|---|
| ArrayList vs LinkedList? | ์๊ฐ ๋ณต์ก๋ + CPU ์บ์ |
| ์ธ์ LinkedList? | ๊ฑฐ์ ์์ (ArrayDeque ๊ฐ ๋ณดํต ๋ ์ข์) |
| ArrayList capacity ๋์? | 1.5๋ฐฐ ํ์ฅ |
| ์ธ๋ฑ์ค ์ ๊ทผ ์ฐจ์ด? | O(1) vs O(n) |
| ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ? | LinkedList ๊ฐ 5๋ฐฐ |
1๏ธโฃ ArrayList = ๋์ ๋ฐฐ์ด, LinkedList = ์ด์ค ์ฐ๊ฒฐ ๋ฆฌ์คํธ.
ArrayList ๋ ์ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ์ ๋ฐฐ์ด ๋ณด๊ด โ ์ธ๋ฑ์ค ์ ๊ทผ O(1), ์ค๊ฐ ์ฝ์ O(n). LinkedList ๋ Node ๊ฐ์ฒด๋ค์ ์๋ฐฉํฅ ์ฌ์ฌ โ ์ธ๋ฑ์ค ์ ๊ทผ O(n), ์ค๊ฐ ์ฝ์ O(1) (ํ์ ํ). Big-O ๋ง ๋ณด๋ฉด LinkedList ๊ฐ ์ข์ ๋ณด์ผ ์๋ ์์ผ๋...
2๏ธโฃ CPU ์บ์ ์นํ๋๊ฐ ๋ชจ๋ ๊ฒ์ ๋ฐ๊พผ๋ค โ ArrayList ๊ฐ ๊ฑฐ์ ํญ์ ๋น ๋ฆ.
ํ๋ CPU ์ L1 ์บ์ (1ns) vs RAM (100ns) ์ 100๋ฐฐ ์ฐจ์ด. ArrayList ๋ ์ฐ์ ๋ฉ๋ชจ๋ฆฌ โ ์บ์ ๋ผ์ธ ํ์ฉ โ ๋ค์ ์์๊ฐ ์ด๋ฏธ ์บ์์. LinkedList ๋ ํฉ์ด์ง Node โ ๋งค๋ฒ ์บ์ ๋ฏธ์ค โ 100๋ฐฐ ๋๋ฆผ. ์์ฐจ ์ํ์์ ArrayList ๊ฐ 5~10๋ฐฐ ๋น ๋ฆ. ๋ฉ๋ชจ๋ฆฌ๋ 5๋ฐฐ ์ ๊ฒ ์ฌ์ฉ.
3๏ธโฃ ์ค๋ฌด ์์น โ ArrayList ๊ฐ ํ์ค, LinkedList ๋ ๊ฑฐ์ ์ ์.
List ๊ฐ ํ์ํ๋ฉด ๋ฌด์กฐ๊ฑด ArrayList. ํฐ ๋ฐ์ดํฐ๋ฉด
new ArrayList<>(์์ํฌ๊ธฐ)๋ก capacity ๋ฏธ๋ฆฌ ์ค์ . ์ ๋ ํ๊ฐ ํ์ํ๋ฉดArrayDeque(LinkedList ๋ณด๋ค ์ข์). ์ํ ์ค ์์ ์removeIf๋๋Iterator.remove. HashMap ํค๋ก ๊ฐ๋ณ List ์ฌ์ฉ X (hashCode ๋ณ๊ฒฝ ์ํ). LinkedList ๋ ๋ฉด์ ๋ต๋ณ ์ธ์๋ ์ฌ์ฉ์ฒ๊ฐ ๊ฑฐ์ ์๋ค๊ณ ๋ด๋ ๋ฌด๋ฐฉ.
Q1: ArrayList ๊ฐ LinkedList ๋ณด๋ค ๊ฑฐ์ ํญ์ ๋น ๋ฅธ ์ด์ ๋ฅผ ๋ฉ๋ชจ๋ฆฌ + CPU ๊ด์ ์์ ์ค๋ช ํ๋ผ.
ํ ์ค ๋ต: ์ฐ์ ๋ฉ๋ชจ๋ฆฌ ๊ฐ CPU ์บ์ ๋ผ์ธ ์ ํจ์จ์ ์ผ๋ก ํ์ฉํด L1 ์บ์ (1ns) ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๊ธฐ ๋๋ฌธ. LinkedList ๋ ํฉ์ด์ง Node ๋ก ๋งค๋ฒ RAM (100ns) ์ ๊ทผ.
์์ธ ์ค๋ช :
ArrayList ์ ๋ฉ๋ชจ๋ฆฌ:
Heap ์ ํ ์์ญ (์ฐ์):
์ฃผ์ 1000: [Item 0]
์ฃผ์ 1004: [Item 1]
์ฃผ์ 1008: [Item 2]
์ฃผ์ 1012: [Item 3]
...
LinkedList ์ ๋ฉ๋ชจ๋ฆฌ:
Heap ์ ํฉ์ด์ง ์์ญ:
์ฃผ์ 1000: [Node 0: prev=null, next=์ฃผ์ 7300, item]
์ฃผ์ 7300: [Node 1: prev=์ฃผ์ 1000, next=์ฃผ์ 4200, item]
์ฃผ์ 4200: [Node 2: prev=์ฃผ์ 7300, next=์ฃผ์ 12000, item]
์ฃผ์ 12000: [Node 3: ...]
์ ํฉ์ด์ง๋?:
new ๋ก ์์ฑL1 Cache (32 KB): ~ 1 ns (CPU ์ฝ์ด ์)
L2 Cache (256 KB): ~ 5 ns
L3 Cache (8 MB): ~ 30 ns (์ฝ์ด ๊ณต์ )
RAM (16 GB): ~ 100 ns
ํต์ฌ: L1 vs RAM = 100๋ฐฐ ์ฐจ์ด.
CPU ๊ฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฝ์ ๋:
ArrayList ์ ํ์ฉ:
๋ฐฐ์ด: [I0][I1][I2][I3][I4][I5][I6][I7]...
โโโโ 64 ๋ฐ์ดํธ ์บ์ ๋ผ์ธ โโโโโ
์ฒ๋ฆฌ:
1. I0 ์ฝ๊ธฐ โ ์บ์ ๋ฏธ์ค โ 64 ๋ฐ์ดํธ ๊ฐ์ ธ์ด (I0~I15)
2. I1 ์ฝ๊ธฐ โ โ ์บ์ ํํธ (1 ns)
3. I2 ์ฝ๊ธฐ โ โ ์บ์ ํํธ (1 ns)
...
17. I16 ์ฝ๊ธฐ โ ์บ์ ๋ฏธ์ค โ ๋ค์ 64 ๋ฐ์ดํธ ๊ฐ์ ธ์ด
ํ๊ท ๋น์ฉ: 100 ns / 16 ์์ = 6 ns per ์์.
์ฒ๋ฆฌ:
1. Node 0 ์ฝ๊ธฐ โ ์บ์ ๋ฏธ์ค โ 100 ns
2. Node 0.next ๋ฐ๋ผ Node 1 ์ฝ๊ธฐ โ ๋ ์บ์ ๋ฏธ์ค โ 100 ns
3. Node 1.next ๋ฐ๋ผ Node 2 ์ฝ๊ธฐ โ ๋ ์บ์ ๋ฏธ์ค โ 100 ns
...
ํ๊ท ๋น์ฉ: 100 ns per ์์ (ArrayList ์ 16๋ฐฐ).
int N = 1_000_000;
// ArrayList
for (Integer i : arrayList) sum += i; // ์ฝ 5 ms
// LinkedList
for (Integer i : linkedList) sum += i; // ์ฝ 50 ms
โ 10๋ฐฐ ์ฐจ์ด.
LinkedList ์ ๊ฐ Node:
private static class Node<E> {
E item; // 4~8 ๋ฐ์ดํธ
Node<E> next; // 4~8 ๋ฐ์ดํธ
Node<E> prev; // 4~8 ๋ฐ์ดํธ
}
// + ๊ฐ์ฒด ํค๋ 16 ๋ฐ์ดํธ
// = ์ฝ 32~40 ๋ฐ์ดํธ per ์์
ArrayList:
Object[] elementData; // 4~8 ๋ฐ์ดํธ per ์ฐธ์กฐ
๋ฉ๋ชจ๋ฆฌ ์ฐจ์ด:
100๋ง ์์:
โ GC ๋ถ๋ด ์ฆ๊ฐ.
| ์ธก๋ฉด | ArrayList | LinkedList |
|---|---|---|
| ๋ฉ๋ชจ๋ฆฌ ๋ฐฐ์น | ์ฐ์ | ํฉ์ด์ง |
| ์บ์ ํ์ฉ | ๋งค์ฐ ๋์ | ๊ฑฐ์ ์์ |
| L1 ํํธ์จ | ~95% | ~5% |
| ํ๊ท ์ ๊ทผ ์๊ฐ | ~6 ns | ~100 ns |
| ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ | 1๋ฐฐ | 5๋ฐฐ |
| GC ๋ถ๋ด | ์ ์ | ํผ |
"์๊ณ ๋ฆฌ์ฆ ๋ณต์ก๋ (Big-O) ๋ ์์์ ์ด์ง ๊ฒฐ์ ์ ์ด ์๋๋ค.
ํ๋ CPU ์์ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ํจํด์ด ์๊ณ ๋ฆฌ์ฆ ๋ณต์ก๋๋ฅผ ์๋ ํ๋ค.
ArrayList ์ ์ฐ์ ๋ฉ๋ชจ๋ฆฌ = CPU ์บ์ ์นํ = ๊ฑฐ์ ๋ชจ๋ ์ค๋ฌด ์๋๋ฆฌ์ค์์ ๋น ๋ฆ.
LinkedList ์ ํฉ์ด์ง Node = ์บ์ ๋ฏธ์ค ์ง์ฅ = ๊ฑฐ์ ๋ชจ๋ ์ค๋ฌด์์ ๋๋ฆผ.
์๋์ด์ ๊นจ๋ฌ์ โ ์ด๋ก ๊ณผ ์ค๋ฌด์ ๊ดด๋ฆฌ๋ฅผ ์ธ์ํ๊ณ ์ธก์ ์ผ๋ก ๊ฒ์ฆ."
Q2: 100๋ง ๊ฐ ๋ฐ์ดํฐ์ ์ค๊ฐ์ 1๋ง ๋ฒ ์ฝ์ ํ๋ค๋ฉด ์ด๋ ๊ฒ ์ข์๊น?
ํ ์ค ๋ต: ๋ ๋ค ๋น์ทํ๊ฒ ๋๋ฆผ (O(nยฒ)), ๊ทธ๋ฌ๋ ArrayList ๊ฐ ์ฝ๊ฐ ๋ ๋น ๋ฆ. ์ด๋ฐ ์๋๋ฆฌ์ค๋ผ๋ฉด ์๋ฃ๊ตฌ์กฐ ์์ฒด๋ฅผ ์ฌ๊ณ ํด์ผ ํจ.
์์ธ ์ค๋ช :
ArrayList ์ค๊ฐ ์ฝ์ :
LinkedList ์ค๊ฐ ์ฝ์ :
โ ๋ ๋ค O(n) โ Big-O ๋ ๊ฐ์.
100๋ง + 1๋ง ๋ฒ ์ฝ์ = ์ฝ 100์ต ๋ฒ์ ์์ = O(nยฒ).
List<Integer> list = ...; // 100๋ง ๊ฐ
for (int i = 0; i < 10000; i++) {
list.add(500000, value); // ์ค๊ฐ์ ์ฝ์
}
์๊ฐ:
ArrayList ์ ์ค๊ฐ ์ฝ์ :
System.arraycopy ์ฌ์ฉ โ ๋งค์ฐ ๋น ๋ฅธ ๋ฉ๋ชจ๋ฆฌ ๋ณต์ฌLinkedList ์ ์ค๊ฐ ์ฝ์ :
node = node.next ํ์โ ArrayList ๊ฐ ์ฝ 25๋ฐฐ ๋น ๋ฆ (์์ ํญ ์ฐจ์ด).
int N = 1_000_000;
int M = 10_000;
// ArrayList
List<Integer> arr = new ArrayList<>(/* N items */);
for (int i = 0; i < M; i++) {
arr.add(N / 2, i);
}
// ์ฝ 30~60์ด
// LinkedList
List<Integer> lnk = new LinkedList<>(/* N items */);
for (int i = 0; i < M; i++) {
lnk.add(N / 2, i);
}
// ์ฝ ๋ถ ๋จ์ (์ฌ์ค์ ์ฌ์ฉ ๋ถ๊ฐ)
์ด๋ฐ ์๋๋ฆฌ์ค์์๋ List ์์ฒด๊ฐ ๋ถ์ ํฉ.
๋์ 1: TreeMap (์ ๋ ฌ ํค)
TreeMap<Integer, Integer> map = new TreeMap<>();
map.put(50, value); // O(log n)
๋์ 2: ์ผ๊ด ์ฒ๋ฆฌ (Batch)
// 1๋ง ๊ฐ๋ฅผ ๋ชจ์์ ํ ๋ฒ์ ์ ๋ ฌ/๋ณํฉ
List<Integer> batch = collect();
list.addAll(batch);
Collections.sort(list); // O(n log n)
๋์ 3: ๋ค๋ฅธ ์๋ฃ๊ตฌ์กฐ (Skip List, B-Tree)
// ์๋ฐ ํ์ค์ ์์, ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์
๋์ 4: ๋ฐ์ดํฐ๋ฒ ์ด์ค
-- DB ์ ์ธ๋ฑ์ค ํ์ฉ โ 100๋ง ํ์์๋ O(log n)
INSERT INTO table VALUES (...);
"100๋ง ๋ฐ์ดํฐ์ 1๋ง ๋ฒ ์ค๊ฐ ์ฝ์ " ์ง๋ฌธ์ด ๋์จ๋ค๋ฉด:
ํ๋ฉด์ ๋ต (1์ ): "LinkedList ๊ฐ ์ฝ์ ์ O(1) ์ด๋ผ ๋ ๋น ๋ฆ"
์ค๊ธ ๋ต (3์ ): "๋ ๋ค O(n) โ LinkedList ๋ ์์น ํ์ O(n) ์ด ์ถ๊ฐ๋จ"
์๋์ด ๋ต (5์ ):
"100๋ง + 1๋ง ์ค๊ฐ ์ฝ์ ์ ์๋ฃ๊ตฌ์กฐ ์ ํ ๋ฌธ์ ๊ฐ ์๋ ์ค๊ณ ๋ฌธ์ .
ArrayList ์ LinkedList ๋ชจ๋ O(nยฒ) ๋ก ๋ถ์ ํฉ.
์ง์ง ์๋์ด๋ '๋ ์ค ์ด๋ ๊ฒ?' ์ด ์๋ '์ ์ด๋ฐ ํจํด์ด ํ์ํ๊ฐ?' ๋ฅผ ๋ฌป๋๋ค.
์์ฃผ ๋ฐ๋๋ ์ ๋ ฌ๋ ๋ฐ์ดํฐ = TreeMap, ์ธ๋ฑ์ค = DB, ๋ถ๋ณ ๋ฐ์ดํฐ = ์ ๋ ฌ ํ ๊ฒ์."