3 분 소요

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는 인덱스가 중요하다.
  • 순서가 정해져있고 고정되어 있는 값이다.
  • 고정되어있는 값을 수정하면 그거에 맞게 인덱스도 수정해서 다시 고정해줘야된다.

댓글남기기