0. 빅오(O) 표기법
빅오(Big O) 표기법은 알고리즘의 성능을 분석할 때 사용하는 수학적 표현 방식이다.
여기서 중요한 것은 알고리즘의 정확한 실행 시간을 계산하는 것이 아니라,
데이터 양의 증가에 따른 성능의 변화 추세를 이해하는 것이다.
빅오 표기법의 예시 O(1) - 상수 시간: 입력 데이터의 크기에 관계없이 알고리즘의 실행 시간이 일정한다.
O(n) - 선형 시간: 알고리즘의 실행 시간이 입력 데이터의 크기에 비례하여 증가한다.
O(n²) - 제곱 시간: 알고리즘의 실행 시간이 입력 데이터의 크기의 제곱에 비례하여 증가한다.
O(log n) - 로그 시간: 알고리즘의 실행 시간이 데이터 크기의 로그에 비례하여 증가한다.
O(n log n) - 선형 로그 시간
0.1. 빅오 표기법 정리
빅오 표기법은 매우 큰 데이터를 입력한다고 가정하고, 데이터 양 증가에 따른 성능의 변화 추세를 비교하는데 사용한다.
쉽게 이야기해서 정확한 성능을 측정하는 것이 목표가 아니라 매우 큰 데이터가 들어왔을 때의 대략적인 추세를 비교를 하는 것이 목적이다.
1. 배열의 특징1 - 배열과 인덱스
자료구조란?
여러 데이터(자료)를 구조화해서 다루는 것
package collection.array;
import java.util.Arrays;
public class ArrayMain1 {
public static void main(String[] args) {
int[] arr = new int[5];
//index 입력: O(1)
System.out.println("==index 입력: O(1)==");
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
System.out.println(Arrays.toString(arr));
//index 변경: O(1)
System.out.println("==index 변경: O(1)==");
arr[2] = 10;
System.out.println(Arrays.toString(arr));
//index 조회: O(1)
System.out.println("==index 조회: O(1)==");
System.out.println("arr[2] = " + arr[2]);
//검색: O(n)
System.out.println("==배열 검색: O(n)==");
System.out.println(Arrays.toString(arr));
int value = 10;
for (int i = 0; i < arr.length; i++) {
System.out.println("arr[" + i + "]:" + arr[i]);
if (arr[i] == value) {
System.out.println(value + " 찾음");
break;
}
}
}
}
// 출력
==index 입력: O(1)==
[1, 2, 3, 0, 0]
==index 변경: O(1)==
[1, 2, 10, 0, 0]
==index 조회: O(1)==
arr[2] = 10
==배열 검색: O(n)==
[1, 2, 10, 0, 0]
arr[0]:1
arr[1]:2
arr[2]:10
10 찾음
1.1. 배열 메모리 그림
- 배열에서 자료를 찾을 때 인덱스(index)를 사용하면 매우 빠르게 자료를 찾을 수 있다.
- 인덱스를 통한 입력, 변경, 조회의 경우 1번의 계산으로 자료의 위치를 찾을 수 있다.
1.2. 배열에서의 검색
배열에 들어있는 특정 데이터를 찾는 행위를 검색이라고 한다.
배열에 들어있는 특정 데이터를 찾을 때는 for loop을 돌면서 데이터를 하나하나 비교해야 한다.
→ 따라서 평균적으로 볼 때 배열의 크기가 클 수록 오랜 시간이 걸린다.
→ 배열의 크기가 n이면 연산도 n만큼 필요하게 된다.
2. 배열의 특징2 - 데이터 추가
데이터를 추가하는 행위는 기존 데이터를 유지하면서 새로운 데이터를 입력한다는 것을 뜻한다.
(만약 여기서 데이터를 중간에 추가하면 기존 데이터가 오른쪽으로 한 칸씩 이동되어야 한다.)
2.1. 첫번째 위치에 데이터 추가 : O(n)
[기존]
[추가]
- 기존 데이터들은 모두 오른쪽으로 한 칸씩 밀어서 추가할 위치를 확보한다.
- 이 때, 배열의 마지막 부분부터 오른쪽으로 밀어야 데이터를 유지할 수 있다.
- 왼쪽의 데이터를 오른쪽으로 대입하는 과정을 반복한다.
2.2. 배열의 중간 위치에 추가 : O(n)
[기존]
[추가]
- 지정한 index에 데이터를 추가해야 하기 때문에 지정한 index를 확보해야 한다.
- 따라서 index부터 시작해서 데이터들을 오른쪽으로 밀어야 한다.
- 이 경우 지정한 index 기준 왼쪽의 데이터들은 이동하지 않아도 된다.
2.3. 배열의 마지막 위치에 추가 : O(1)
- 이 경우 기존의 데이터를 이동하지 않아도 되며 단순하게 값만 입력하면 된다.
2.4. 코드로 구현해보기
package collection.array;
import java.util.Arrays;
public class ArrayMain2 {
public static void main(String[] args) {
// Q. int[]는 Wrapper class가 아니기 때문에 기본 method 밖에 없는건가?
// A. 이것을 배열이라고 한다..? (Array)
int[] arr = new int[5];
arr[0] = 1;
arr[1] = 2;
System.out.println("배열의 첫번째 위치에 3 추가 o(n)");
int newValue = 3;
addFirst(arr, newValue);
System.out.println(Arrays.toString(arr));
System.out.println("배열의 특정 위치에 추가 o(n)");
int newValue2 = 4;
addAtIndex(arr, 3, newValue2);
System.out.println(Arrays.toString(arr));
System.out.println("배열의 마지막 위치에 추가 o(1)");
int newValue3 = 5;
addLast(arr, newValue3);
System.out.println(Arrays.toString(arr));
}
private static void addFirst(int[] arr, int value) {
for (int i = arr.length - 1; i > 0; i--) {
arr[i] = arr[i - 1];
}
arr[0] = value;
}
private static void addAtIndex(int[] arr, int index, int value) {
for (int i = arr.length - 1; i > index; i--) {
arr[i] = arr[i - 1];
}
arr[index] = value;
}
private static void addLast(int[] arr, int value) {
arr[arr.length - 1] = value;
}
}
// 결과
배열의 첫번째 위치에 3 추가 o(n)
[3, 1, 2, 0, 0]
배열의 특정 위치에 추가 o(n)
[3, 1, 2, 4, 0]
배열의 마지막 위치에 추가 o(1)
[3, 1, 2, 4, 5]
Process finished with exit code 0
2.5. 배열의 한계
배열은 가장 기본적인 자료 구조이고, 특히 인덱스를 사용할 때 최고의 효율이 나온다.
하지만 이런 배열에는 큰 단점이있다.
바로 배열의 크기를 배열을 생성하는 시점에 미리 정해야 한다는 점이다.
배열처럼 처음부터 정적으로 길이가 정해져있는 것이 아니라,
동적으로 언제든지 길이를 늘리고 줄일 수 있는 자료 구조가 있다면 편리할 것이다.
3. 직접 구현하는 배열 리스트 - 시작해보기
**배열(Array)**의 경우에는 다음의 단점들이 존재했다.
- 배열의 길이를 동적으로 변경할 수 없음
- 데이터를 추가하기 불편하며, 배열 추가하는 코드를 직접 작성해야 한다.
→ 배열의 단점을 해결하기 위한 자료 구조를 List(리스트)라고 한다.
List 자료구조 : 순서가 있고 중복을 허용하는 자료 구조
**배열과 리스트의 차이점
- 배열 : 순서가 있고 중복을 허용하지만 크기가 정적으로 고정된다.
- 리스트 : 순서가 있고 중복을 허용하지만 크기가 동적으로 변할 수 있다.**
3.1. MyArrayList 구현 - V1
package collection.array;
import java.util.Arrays;
public class MyArrayListV1 {
// 배열의 크기
private static final int DEFAULT_CAPACITY = 5;
// 다양한 타입의 데이터를 보관하기 위해 Object 타입의 배열을 사용!
private Object[] elementData;
// 리스트의 크기
private int size = 0;
public MyArrayListV1() {
elementData = new Object[DEFAULT_CAPACITY];
}
public MyArrayListV1(int initialCapacity) {
elementData = new Object[initialCapacity];
}
// 사이즈 조회
public int size() {
return size;
}
// 리스트에 데이터를 추가
public void add(Object e) {
elementData[size] = e;
size++;
}
// 인덱스에 있는 항목 조회
public Object get(int index) {
return elementData[index];
}
// 인덱스에 있는 항목을 변경
public Object set(int index, Object element) {
Object oldValue = get(index);
elementData[index] = element;
return oldValue;
}
// 검색 기능
public int indexOf(Object o) {
for (int i = 0; i < size; i++) {
if (o.equals(elementData[i])) {
return i;
}
}
return -1;
}
// Arrays.copyOf(elementData, size) : size 크기의 배열을 새로 만듬
// toString() 출력 시 의미 없는(none) 값을 출력하지 않기 위해 사용함
@Override
public String toString() {
return Arrays.toString(Arrays.copyOf(elementData, size)) + " size=" +
size + ", capacity=" + elementData.length;
}
}
Main 클래스
package collection.array;
public class MyArrayListV1Main {
public static void main(String[] args) {
MyArrayListV1 list = new MyArrayListV1();
System.out.println("==데이터 추가==");
System.out.println(list);
list.add("a");
System.out.println(list);
list.add("b");
System.out.println(list);
list.add("c");
System.out.println(list);
System.out.println("==기능 사용==");
System.out.println("list.size(): " + list.size());
System.out.println("list.get(1): " + list.get(1));
System.out.println("list.indexOf('c'): " + list.indexOf("c"));
System.out.println("list.set(2, 'z'), oldValue: " + list.set(2, "z"));
System.out.println(list);
System.out.println("==범위 초과==");
list.add("d");
System.out.println(list);
list.add("e");
System.out.println(list);
//범위 초과, capacity가 늘어나지 않으면 예외 발생
list.add("f");
System.out.println(list);
}
}
// 결과
==데이터 추가==
[] size=0, capacity=5
[a] size=1, capacity=5
[a, b] size=2, capacity=5
[a, b, c] size=3, capacity=5
==기능 사용==
list.size(): 3
list.get(1): b
list.indexOf('c'): 2
list.set(2, 'z'), oldValue: c
[a, b, z] size=3, capacity=5
==범위 초과==
[a, b, z, d] size=4, capacity=5
[a, b, z, d, e] size=5, capacity=5
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
at collection.array.MyArrayListV1.add(MyArrayListV1.java:30)
at collection.array.MyArrayListV1Main.main(MyArrayListV1Main.java:31)
3.1.1. 데이터 추가
list.add("a") 수행 시, [a] size=1, capacity=5
list.add(”b”) 수행 시, [a, b] size=2, capacity=5
3.1.2. 데이터 변경
list.set(2, "z") 수행 시, [a, b, z] size=3, capacity=5
3.1.3. 범위 초과 시
size가 배열의 크기인 capacity에 도달하면 데이터를 더이상 추가할 수 없고 예외가 발생하게 된다.
4. 직접 구현하는 배열 리스트 - 동적 배열
package collection.array;
import java.util.Arrays;
public class MyArrayListV2 {
// 배열의 크기
private static final int DEFAULT_CAPACITY = 5;
// 다양한 타입의 데이터를 보관하기 위해 Object 타입의 배열을 사용!
private Object[] elementData;
// 리스트의 크기
private int size = 0;
public MyArrayListV2() {
elementData = new Object[DEFAULT_CAPACITY];
}
public MyArrayListV2(int initialCapacity) {
elementData = new Object[initialCapacity];
}
// 사이즈 조회
public int size() {
return size;
}
// 리스트에 데이터를 추가
public void add(Object e) {
// V1에 비해 추가된 부분
if (size == elementData.length) {
grow();
}
elementData[size] = e;
size++;
}
// V1에 비해 추가된 메서드
private void grow() {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity * 2;
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 인덱스에 있는 항목 조회
public Object get(int index) {
return elementData[index];
}
// 인덱스에 있는 항목을 변경
public Object set(int index, Object element) {
Object oldValue = get(index);
elementData[index] = element;
return oldValue;
}
// 검색 기능
public int indexOf(Object o) {
for (int i = 0; i < size; i++) {
if (o.equals(elementData[i])) {
return i;
}
}
return -1;
}
// Arrays.copyOf(elementData, size) : size 크기의 배열을 새로 만듬
// toString() 출력 시 의미 없는(none) 값을 출력하지 않기 위해 사용함
@Override
public String toString() {
return Arrays.toString(Arrays.copyOf(elementData, size)) + " size=" +
size + ", capacity=" + elementData.length;
}
}
- 추가된 부분
- add() : 데이터 추가하기 전에 size와 element.length를 비교하여 grow() 메서드 호출
- grow() : 기존 배열을 기반으로 새로운 배열을 만들고 크기를 2배 늘려준다.
- Arrays.copyOf(기존배열, 새로운길이) : 새로운 길이로 배열을 생성하고 기존 배열의 값을 그대로 복사해준다.
Main 코드
package collection.array;
public class MyArrayListV2Main {
public static void main(String[] args) {
MyArrayListV2 list = new MyArrayListV2(2);
System.out.println(list);
list.add("a");
System.out.println(list);
list.add("b");
System.out.println(list);
list.add("c");
System.out.println(list);
list.add("d");
System.out.println(list);
list.add("e");
System.out.println(list);
list.add("f");
System.out.println(list);
}
}
// 출력
[] size=0, capacity=2
[a] size=1, capacity=2
[a, b] size=2, capacity=2
[a, b, c] size=3, capacity=4
[a, b, c, d] size=4, capacity=4
[a, b, c, d, e] size=5, capacity=8
[a, b, c, d, e, f] size=6, capacity=8
4.1. 배열의 크기를 초과하면 어찌 되는가?
[1] add() 메서드가 호출될 때 size가 capacity에 초과하게 된다.
[2] 초과하면 grow() 메서드가 호출되며 2배 큰 크기로 새로운 배열이 생성된다.
[3] 새로운 배열에 값을 복사한다.
[4] elementData가 바라봐야 할 참조값을 변경한다.
[5] 그제서야 add()메서드는 데이터를 추가한다.
이후에는 GC에 의해 기존 elementData가 바라보던 배열은 참조하는 값이 없으므로 GC 대상이 된다.
(참고)
예제를 단순화하기 위해 여기선 2배씩 증가시켰지만, 보통 50% 정도 증가하는 방법을 사용한다고 한다.
5. 직접 구현하는 배열 리스트3 - 기능 추가
- add(index, 데이터) : index 위치에 데이터를 추가한다.
- remove(index) : index 위치의 데이터를 삭제한다.
앞서 만든 add()는 구현이 단순했다.
하지만 앞이나 중간에 데이터를 추가하거나 삭제하면 기존 데이터의 위치들을 이동시키고 데이터를 추가해야 하기 때문에 구현이 복잡해진다.
→ O(n)으로 표현할 수 있다.
package collection.array;
import java.util.Arrays;
public class MyArrayListV3 {
// 배열의 크기
private static final int DEFAULT_CAPACITY = 5;
// 다양한 타입의 데이터를 보관하기 위해 Object 타입의 배열을 사용!
private Object[] elementData;
// 리스트의 크기
private int size = 0;
public MyArrayListV3() {
elementData = new Object[DEFAULT_CAPACITY];
}
public MyArrayListV3(int initialCapacity) {
elementData = new Object[initialCapacity];
}
// 사이즈 조회
public int size() {
return size;
}
public void add(Object e) {
if (size == elementData.length) {
grow();
}
elementData[size] = e;
size++;
}
// V2에 비해 코드 추가된 부분
public void add(int index, Object e) {
if (size == elementData.length) {
grow();
}
shiftRightFrom(index);
elementData[index] = e;
size++;
}
// V2에 비해 추가된 부분
// 요소의 마지막부터 index까지 오른쪽으로 한칸씩 밀어주기
private void shiftRightFrom(int index) {
for (int i = size; i > index; i--) {
elementData[i] = elementData[i - 1];
}
}
private void grow() {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity * 2;
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 인덱스에 있는 항목 조회
public Object get(int index) {
return elementData[index];
}
// 인덱스에 있는 항목을 변경
public Object set(int index, Object element) {
Object oldValue = get(index);
elementData[index] = element;
return oldValue;
}
// V2에 비해 추가된 메서드
public Object remove(int index) {
Object oldValue = get(index);
shiftLeftFrom(index);
size--;
elementData[size] = null;
return oldValue;
}
// V2에 비해 추가된 메서드
// 요소의 index부터 마지막까지 왼쪽으로 밀기
private void shiftLeftFrom(int index) {
for (int i = index; i < size - 1; i++) {
elementData[i] = elementData[i + 1];
}
}
// 검색 기능
public int indexOf(Object o) {
for (int i = 0; i < size; i++) {
if (o.equals(elementData[i])) {
return i;
}
}
return -1;
}
// Arrays.copyOf(elementData, size) : size 크기의 배열을 새로 만듬
// toString() 출력 시 의미 없는(none) 값을 출력하지 않기 위해 사용함
@Override
public String toString() {
return Arrays.toString(Arrays.copyOf(elementData, size)) + " size=" +
size + ", capacity=" + elementData.length;
}
}
Main 코드
package collection.array;
public class MyArrayListV3Main {
public static void main(String[] args) {
MyArrayListV3 list = new MyArrayListV3();
//마지막에 추가 //O(1)
list.add("a");
list.add("b");
list.add("c");
System.out.println(list);
//원하는 위치에 추가
System.out.println("addLast");
list.add(3, "addLast"); //O(1)
System.out.println(list);
System.out.println("addFirst");
list.add(0, "addFirst"); //O(n)
System.out.println(list);
//삭제
Object removed1 = list.remove(4);//remove Last O(1)
System.out.println("remove(4)="+ removed1);
System.out.println(list);
Object removed2 = list.remove(0);//remove First O(n)
System.out.println("remove(0)=" + removed2);
System.out.println(list);
}
}
// 결과
[a, b, c] size=3, capacity=5
addLast
[a, b, c, addLast] size=4, capacity=5
addFirst
[addFirst, a, b, c, addLast] size=5, capacity=5
remove(4)=addLast
[addFirst, a, b, c] size=4, capacity=5
remove(0)=addFirst
[a, b, c] size=3, capacity=5
6. 직접 구현하는 배열 리스트4 - 제네릭
앞서 만든 MyArrayList 들은 Object 를 입력받기 때문에 아무 데이터나 입력할 수 있고, 또 결과로 Object 를 반환한다.
따라서 필요한 경우 다운 캐스팅을 해야하고, 또 타입 안전성이 떨어지는 단점이 있다.
package collection.array;
public class MyArrayListV3BadMain {
public static void main(String[] args) {
MyArrayListV3 numberList = new MyArrayListV3();
// 숫자만 입력 하기를 기대
numberList.add(1);
numberList.add(2);
numberList.add("문자3"); //문자를 입력
System.out.println(numberList);
// Object를 반환하므로 다운 캐스팅 필요
Integer num1 = (Integer) numberList.get(0);
Integer num2 = (Integer) numberList.get(1);
// ClassCastException 발생, 문자를 Integer로 캐스팅
Integer num3 = (Integer) numberList.get(2);
}
}
// 결과
[1, 2, 문자3] size=3, capacity=5
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
at collection.array.MyArrayListV3BadMain.main(MyArrayListV3BadMain.java:14)
- Object를 받아서 저장하기 때문에 자바의 모든 타입을 다 저장할 수 있다.
- 따라서 숫자를 입력하다가 문자를 입력해도 자바 입장에서 아무런 문제가 되지 않는다.
- 값을 꺼낼 때 Object를 반환하기 때문에 원래 입력한 타입으로 받으려면 다운 캐스팅을 해야한다.
- 입력한 데이터를 정확히 알고 있지 않으면 예외가 발생할 수 있다.
→ 제네릭을 도입하면 타입 안정성을 확보할 수 있게 된다.
package collection.array;
import java.util.Arrays;
public class MyArrayListV4<E> {
// 배열의 크기
private static final int DEFAULT_CAPACITY = 5;
// 다양한 타입의 데이터를 보관하기 위해 Object 타입의 배열을 사용!
private Object[] elementData;
// 리스트의 크기
private int size = 0;
public MyArrayListV4() {
elementData = new Object[DEFAULT_CAPACITY];
}
public MyArrayListV4(int initialCapacity) {
elementData = new Object[initialCapacity];
}
// 사이즈 조회
public int size() {
return size;
}
public void add(E e) {
if (size == elementData.length) {
grow();
}
elementData[size] = e;
size++;
}
// V2에 비해 코드 추가된 부분
public void add(int index, E e) {
if (size == elementData.length) {
grow();
}
shiftRightFrom(index);
elementData[index] = e;
size++;
}
// V2에 비해 추가된 부분
// 요소의 마지막부터 index까지 오른쪽으로 한칸씩 밀어주기
private void shiftRightFrom(int index) {
for (int i = size; i > index; i--) {
elementData[i] = elementData[i - 1];
}
}
private void grow() {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity * 2;
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 인덱스에 있는 항목 조회
@SuppressWarnings("unchecked")
public E get(int index) {
return (E) elementData[index];
}
// 인덱스에 있는 항목을 변경
public E set(int index, E element) {
E oldValue = get(index);
elementData[index] = element;
return oldValue;
}
// V2에 비해 추가된 메서드
public E remove(int index) {
E oldValue = get(index);
shiftLeftFrom(index);
size--;
elementData[size] = null;
return oldValue;
}
// V2에 비해 추가된 메서드
// 요소의 index부터 마지막까지 왼쪽으로 밀기
private void shiftLeftFrom(int index) {
for (int i = index; i < size - 1; i++) {
elementData[i] = elementData[i + 1];
}
}
// 검색 기능
public int indexOf(E o) {
for (int i = 0; i < size; i++) {
if (o.equals(elementData[i])) {
return i;
}
}
return -1;
}
// Arrays.copyOf(elementData, size) : size 크기의 배열을 새로 만듬
// toString() 출력 시 의미 없는(none) 값을 출력하지 않기 위해 사용함
@Override
public String toString() {
return Arrays.toString(Arrays.copyOf(elementData, size)) + " size=" +
size + ", capacity=" + elementData.length;
}
}
- MyArrayListV4<E> : 제네릭 타입 선언
- Object로 입력받고 출력했던 곳을 매개변수 E로 변경
- Object[] elementData는 그대로 유지
- Why ? 제네릭은 런타임에 이레이저에 의해 타입 정보가 사라진다. 따라서 런타임에 타입 정보가 필요한 생성자에 사용할 수 없고 Object를 그대로 사용해야 한다.
private Object[] elementData; ... ... public MyArrayListV4() { elementData = new Object[DEFAULT_CAPACITY]; } public MyArrayListV4(int initialCapacity) { elementData = new Object[initialCapacity]; }
- Object[] elementData를 그대로 유지해도 괜찮을까?
- 데이터 추가 시 : <E> 타입만 추가할 수 있도록 강제되기 때문에 OK
- 데이터 조회 시 : 데이터를 꺼낼 때 항상 (E)로 다운 캐스팅을 한다. <E> 타입만 추가할 수 있도록 강제되었기 때문에 (E)로 다운 캐스팅해도 문제가 되지 않는다.
Main 코드
package collection.array;
public class MyArrayListV4Main {
public static void main(String[] args) {
MyArrayListV4<String> stringList = new MyArrayListV4<>();
stringList.add("a");
stringList.add("b");
stringList.add("c");
String string = stringList.get(0);
System.out.println("string = " + string);
MyArrayListV4<Integer> intList = new MyArrayListV4<>();
intList.add(1);
intList.add(2);
intList.add(3);
Integer integer = intList.get(0);
System.out.println("integer = " + integer);
}
}
**생성자에는 제네릭의 타입 매개변수를 사용할 수 없는 한계가 있다. 따라서 배열을 생성할 때 대안으로 Object 배열을 사용해야 한다.
하지만 제네릭이 데이터를 입력받고 반환하는 곳의 타입을 고정해주기 때문에 고정된 타입으로 Object 배열에 데이터를 보관할 수 있게 되며 이로 인해 데이터를 꺼낼 때도 고정된 타입으로 안전하게 다운 캐스팅을 할 수 있게 된다.**
배열 리스트는 중간에 데이터를 추가하거나 삭제할 때 성능이 좋지 않다. O(n)
따라서 이런 단점을 해결한 링크드 리스트(Linked List)를 다음 시간에 알아보자..
'Programming > Java' 카테고리의 다른 글
[Java] 김영한의 자바 중급 2편 #5 - List (1) | 2024.11.27 |
---|---|
[Java] 김영한의 자바 중급 2편 #4 - LinkedList (0) | 2024.11.11 |
[Java] 김영한의 자바 중급 2편 #2 - 제네릭2 (1) | 2024.11.09 |
[Java] 김영한의 자바 중급 2편 #1 - 제네릭 (1) | 2024.11.03 |
[Java] 김영한의 자바 중급 1편 #10 - 예외처리 실습 (0) | 2024.10.15 |