ArrayList 뜯어보기!
- 언제나 기본기가 제일 중요함
- 그렇다면? 구조 뜯어 버리기!
우선 기본적으로 많이 쓰는 클래스를 구현해 보자!
package list;
public interface IList<T> {
// 1. ArrayList 와 LinkedList 에서 같이 쓸 수 있도록 인터페이스 구현
// 2. 어떤 타입이 올지 모르니 제네릭선언,Type 의 약자로 보통 <T>를 쓴다고함
// 3. 상황에 따라 Key 값은 K value 값은 V를, 상황에 따라 다양하게 씀
// 4. 그래도 혹시모르니 웬만하면 주석을 달아주는 습관을 갖는 게 좋을듯함
void add(T t);
void insert(int index, T t);
void clear();
boolean delete(T t);
boolean deleteByIndex(int index);
T get(int index);
int indexOf(T t);
boolean isEmpty();
boolean contains(T t);
int size();
}
package list;
import java.util.Arrays;
public class MyArrayList<T> implements IList<T> {
// 초기화 시켜주는 생성자
private static final int DEFAULT_SIZE = 50;
private int size; //데이터가 몇 개 들어가는지 확인해주는 변수
private T[] elements;
public MyArrayList() {
this.size = 0; // 초기화되는 시점의 사이즈
// this.elements = (T[]) new Object[50];
// 위와 같은 코드는 유지 보수에 별로 안 좋음
this.elements = (T[]) new Object[DEFAULT_SIZE];
// 파이썬과 다르게 배열을 선언할 때
// 자바는 처음에 고정된 배열의 사이즈를 처음에 할당줘야함
}
@Override
public void add(T t) {
if (this.size == this.elements.length) {
// 초기에 할당한 사이즈가 꽉 찼는지 검사해줌
this.elements = Arrays.copyOf(this.elements, this.size * 2);
} // 인덱스가 꽉 찼을 경우 이전의 배열을 복사한 후 인덱스 크기를 늘려줌
this.elements[size++] = t;
// ++ 위치가 size 앞에 있는 경우와 뒤에 있는 경우에 따라 연산이 다름
// 증감 연산자
}
@Override
public void insert(int index, T t) {
// add 와는 다르게 insert는 정해준 index 안에 데이터를 넣기 때문에
// index 파라미터를 하나 더 받아야함
if (this.size == this.elements.length) {
this.elements = Arrays.copyOf(this.elements, this.size * 2);
// 위 코드와 마찬가지로 배열이 꽉차있는 경우를 사전에 검사하고
// 꽉 찼다면 사이즈를 늘려줌
}
for (int i = index; i < this.size; i++) {
// 인덱스 5에 NICE 라는 데이터르 넣고 싶을 때
// 인덱스 5에 B 라는 데이터가 있고
// 인덱스 6에 C 라는 데이터가 있다면
// 인덱스 5부터 반복문이 실행됨
this.elements[i+1] = this.elements[i];
// 인덱스 5에 있던 B 데이터는 인텍스 6으로
// 인텍스 6에 있던 C 데이터는 인덱스 7으로
// 이런 식으로 순차적으로 진행
}
this.elements[index] = t;
// 위에 코드들이 진행된 후에 이 코드가 실행되므로
// 아까 인덱스 5에는 데이터가 비어있음
// 이 때 넣고 싶은 데이터 NICE를 넣어줌
// 안그러면 단순히 인덱스 5에는 NICE
// 인덱스 6에는 C 데이터가 들어가므로 기존의 B라는 데이터는 날라감
this.size++;
// 위의 예를 보면 현재 사이즈는 7로 하나가 추가 되었으니.
// 추가된 사이즈만큼 올려줌
// 데이터를 여러개 바꾸는 경우에는 ++ 보다는
// length 같은 걸 사용해야될 거같음
}
@Override
public void clear() {
this.size = 0;
this.elements = (T[]) new Object[DEFAULT_SIZE];
}
@Override
public boolean delete(T t) {
for (int i = 0; i < this.size; i++) {
// 처음부터 끝까지 데이터 검사
if (this.elements[i].equals(t)) {
// for 문이 돌아가는 도중에 인덱스 i의 데이터가
// t와 동일하다면 밑에 for문이 돌아감
// 동일하지 않다면 밑에 반복문은 실행되지 않고 위의 반복문 실행
for (int j = i; j < this.size -1; j++) {
// deleteByIndex 와 같은 원리로 지워준 인덱스만큼
// 다른 인덱스를 앞으로 땡겨옴
this.elements[j] = this.elements[j+1];
}
this.size--;
return true; }
}
return false;
}
@Override
public boolean deleteByIndex(int index) {
if (index < 0 || index > this.size - 1) {
// 유효한 인덱스인지 검사
return false;
}
for (int i = index; i < this.size-1; i++) {
this.elements[i] = this.elements[i+1];
// 삭제가 되었으니 위와 같은 논리로 인덱스를 다시 줄여줌
// this.size-1을 하는 이유는 해당 코드는 1개씩 인덱스를 지워가는데
// 전체 크기가 7이였으면 6이 되므로 기존 사이즈에서 -1을 해줘야함
}
this.size--;
// 위의 코드가 돌아갔다고 가정한다면
// 인덱스 1개가 줄어들어서 size 변수에서 1을 빼줘야함
return true; }
@Override
public T get(int index) {
if (this.size <= index) {
// 유효한 인덱스인지 검사
throw new IndexOutOfBoundsException();
// 유효한 인덱스가 아닐 경우
// 리턴타입이 T이기 때문에
// return false가 아니라 오류 발생시켜주면 됨
}
return this.elements[index];
}
@Override
public int indexOf(T t) {
for(int i = 0; i < this.size; i++) {
if (this.elements[i].equals(t)) {
return i;
// 데이터 값의 인덱스를 반환해줌
}
}
return -1;
// 파이썬에서는 데이터가 [1,2,3] 이렇게 있을경우
// 인덱스 -1을 하면 거꾸로 되서 3을 가져온다
// 자바에서는 음수 인덱스가 사용불가니까 주의하자
}
@Override
public boolean isEmpty() {
return this.size == 0;
// return 값을 애초에 이렇게 하면 True , False 값이
// 알아서 자동으로 리턴됨
}
@Override
public boolean contains(T t) {
for(T elem : this.elements) {
// indexOf와 같은 개념인데 return 값만 T,F로 반환
if (elem.equals(t)) {
return true;
}
}
return false;
}
@Override
public int size() {
return this.size;
}
}
요약
- ArrayList는 인덱스가 중요하다.
- 순서가 정해져있고 고정되어 있는 값이다.
- 고정되어있는 값을 수정하면 그거에 맞게 인덱스도 수정해서 다시 고정해줘야된다.
댓글남기기