본문 바로가기

# 02/Java

[윤성우 열혈자바] 23-2. List<E> 인터페이스를 구현하는 컬렉션 클래스들

반응형

List<E> 인터페이스


List<E> 인터페이스를 구현하는 대표적인 컬렉션 클래스 둘은 다음과 같다.

  • ArrayList<E>             배열 기반 자료구조, 배열을 이용하여 인스턴스 저장
  • LinkedList<E>            리스트 기반 자료구조, 리스트를 이용하여 인스턴스 저장



List<E> 인터페이스를 구현하는 컬렉션 클래스들의 공통 특성

  • 인스턴스의 저장 순서 유지
  • 동일 인스턴스의 중복 저장을 허용한다.


배열 기반의 장단점 


배열은 크기가 고정되어 있어서 만약 원소를 추가하고 싶으면 사이즈가 큰 배열을 새로 만들고 기존의 배열의 원소를 차례대로 옮겨준 다음 원소를 추가해줘야 함! 기존 배열 삭제도 해야됨!

리스트는 그냥 추가하면 됨


단순저장은 배열은 그냥 저장하면 되지만 리스트는 선으로 연결 해줘야 되서 더 느림!








ArrayList<E> 클래스


public static void main(String[] args) {

List<String> list = new ArrayList<>();            // 컬렉션 인스턴스 생성


// 컬렉션 인스턴스에 문자열 인스턴스 저장

list.add("Toy");

list.add("Box");

list.add("Robot");


// 저장된 문자열 인스턴스의 참조

for (int i = 0; i < list.size(); i++)

System.out.print(list.get(i) + '\t');

System.out.println();


list.remove(0);    // 첫 번째 인스턴스 삭제


// 첫 번째 인스턴스 삭제 후 나머지 인스턴스들을 참조

for (int i = 0; i< list.size(); i++)

System.out.print(list.get(i) + '\t');

System.out.println();

}


배열 기반 자료구조이지만 공간의 확보 및 확장은 ArrayList 인스턴스가 스스로 처리한다.









LinkedList<E> 클래스


public static void main(String[] args) {

List<String> list = new LinkedList<>();            // 유일한 변화!!!


// 컬렉션 인스턴스에 문자열 인스턴스 저장

list.add("Toy");

list.add("Box");

list.add("Robot");

// 저장된 문자열 인스턴스의 참조

for(int i = 0; i < list.size(); i++)

System.out.print(list.get(i) + '\t');

System.out.println();


list.remove(0);    // 첫 번째 인스턴스 삭제


// 첫 번째 인스턴스 삭제 후 나머지 인스턴스들을 참조

for(int i = 0; i < list.size(); i++)

System.out.print(list.get(i) + '\t');

System.out.println();

}




리스트 기반 자료구조는 열차 칸을 더하고 빼는 형태의 자료구조이다.

    • 인스턴스 저장         열차 칸을 하나 더한다.
    • 인스턴스 삭제         해당 열차 칸을 삭제한다.








ArrayList<E> vs LinkedList<E>


ArrayList<E>의 단점

- 저장 공간을 늘리는 과정에서 시간이 비교적 많이 소요된다.

- 인스턴스의 삭제 과정에서 많은 연산이 필요할 수 있다. 따라서 느릴 수 있다.


ArrayList<E>의 장점

- 저장된 인스턴스의 참조가 빠르다.


LinkedList<E>의 단점

- 저장된 인스턴스의 참조 과정이 배열에 비해 복잡하다. 따라서 느릴 수 있다.


LinkedList<E>의 장점

- 저장 공간을 늘리는 과정이 간단하다.

- 저장된 인스턴스의 삭제 과정이 단순하다.








저장된 인스턴스의 순차적 접근 방법 1 : enhanced for 문의 사용


public static void main(String[] args) {

List<String> list = new LinkedList<>();


// 인스턴스 저장

list.add("Toy");

list.add("Box");

list.add("Robot");


// 전체 인스턴스 참조

for(String s : list)

System.out.print(s + '\t');


. . . . 

}



for-each 문의 대상이 되기 위한 조건


Iterable<T> 인터페이스의 구현


for-each문은 반복자 기반의 반복문으로 바뀜!!




public interface Collection<E> extends Iterable<E>









저장된 인스턴스의 순차적 접근 방법 2


public static void main(String[] args) {

List<String> list = new LinkedList<>();


. . . .


Iterator<String> itr = list.iterator();    // 반복자 획득, itr이 지팡이를 참조한다.


, , , , 


// 반복자를 이용한 순차적 참조

while( itr.hasNext() ) {    // next 메소드가 반환할 대상이 있다면,

str = itr.next();            // next 메소드를 호출한다.

. . . .

}

}



public interface Iterable<T> {

Iterator<T> iterator();


. . . .

}








Iterator 반복자의 세 가지 메소드


E next()                            다음 인스턴스의 참조 값을 반환

boolean hasNext()              next 메소드 호출 시 참조 값 반환 가능 여부 확인

void remove()                    next 메소드 호출을 통해 반환했던 인스턴스 삭제




// 반복자를 이용한 참조 과정 중 인스턴스의 삭제

while(itr.hasNext()) {

str = itr.next();

if(str.equals("Box"))

itr.remove();        // 위에서 next 메소드가 반환한 인스턴스 삭제

}









배열보다는 컬렉션 인스턴스가 좋다. : 컬렉션 변환


다음 두 가지 이유로 배열보다 ArrayList<E>가 더 좋다.

인스턴스의 저장과 삭제가 편하다.

반복자를 쓸 수 있다.



단, 배열처럼 선언과 동시에 초기화가 불가능하다. 그러나 다음 방법을 쓸 수 있다.

List<String> list = Arrays.asList("Toy", "Robot", "Box");

-> 인자로 전달된 인스턴스들을 저장한 컬렉션 인스턴스의 생성 및 반환

-> 이렇게 생성된 리스트 인스턴스는 Immutable 인스턴스이다. - 참조만 가능, 수정,삭제 안됨!!


다음 생성자를 통해서 새로운 ArrayList 인스턴스 생성 가능

public ArrayList(Collection<? extends E> c) { . . . }

-> Collection<E>를 구현한 컬렉션 인스턴스를 인자로 전달받는다.

-> 그리고 E는 인스턴스 생성 과정에서 결정되므로 무엇이든 될 수 있다.

-> 덧붙여서 매개변수 c로 전달된 컬렉션 인스턴스에서는 참조만(꺼내기만) 가능하다. - 안정적으로 복사하기 위한 것임!!


public static void main(String[] args) {

List<String> list = Arrays.asList("Toy", "Box", "Robot", "Box"); - 어차피 꺼내기만 가능!!


// 생성자 Public ArrayList(Collection<? extends E> c)를 통한 인스턴스 생성

list = new ArrayList<>(list);

. . . .

}


대다수 컬렉션 클래스들은 다른 컬렉션 인스턴스를 인자로 전달받는 생성자를 가지고 있어서, 


다른 컬렉션 인스턴스에 저장된 데이터를 복사해서 새로운 컬렉션 인스턴스를 생성할 수 있다.








배열 기반 리스트를 연결 기반 리스트로...


public ArrayList(Collection<? extends E> c)            // ArrayList<E> 생성자 중 하나

-> 인자로 전달된 컬렉션 인스턴스로부터 ArrayList<E> 인스턴스 생성




public LinkedList(Collection<? extends E> c)        // LinkedList<E> 생성자 중 하나

-> 인자로 전달된 인스턴스로부터 LinkedList<E> 인스턴스 생성




public static void main(String[] args) {

List<String> list = Arrays.asList("Toy", "Box", "Robot", "Box");

list = new ArrayList<>(list);

. . . 

// ArrayList<E> 인스턴스 기반으로 LinkedList<E> 인스턴스 생성

list = new LinkedList<>(list);

. . .

}









기본 자료형 데이터의 저장과 참조


public static void main(String[] args) {

LinkedList<Integer> list = new LinkedList<>();

list.add(10);            // 저장 과정에서 오토 박싱 진행

list.add(20);

list.add(30);


오토 박싱과 오토 언박싱 덕분에 컬렉션 인스턴스에 기본 자료형의 값도 저장 가능하다.


int n;

for(Iterator<Integer> itr = list.iterator(); itr.hasNext(); ) {

n = itr.next();            // 오토 언박싱 진행

System.out.print(n + "\t");

}

System.out.println();

}








리스트만 갖는 양방향 반복자


public ListIterator<E> listIterator()    // List<E> 인터페이스의 메소드

-> ListIterator<E>는 Iterator<E>을 상속한다.




E next()                       다음 인스턴스의 참조 값을 반환

boolean hasNext()         next 메소드 호출 시 참조 값 반환 가능 여부 확인

void remove()               next 메소드 호출을 통해 반환했던 인스턴스를 삭제


E previous()                  next 메소드와 기능은 같고 방향만 반대

boolean hasPrevious()    hasNext 메소드와 기능은 같고 방향만 반대


void add(E e)                인스턴스의 추가

void set(E e)                 인스턴스의 변경








양방향 반복자의 예


public static void main(String[] args) {


List<String> list = Arrays.asList("Toy", "Box", "Robot", "Box");

list = new ArrayList<>(list);


ListIterator<String> litr = list.listIterator();        // 양방향 반복자 획득


String str;

while(litr.hasNext()) {                                    // 왼쪽에서 오른쪽으로 이동을 위한 반복문

str = litr.next();

System.out.print(str+'\t');

if(str.equals("Toy"))                                // "Toy" 만나면 "Toy2" 저장

litr.add("Toy2");

}


System.out.println();


while(litr.hasPrevious()) {                             // 오른쪽에서 왼쪽으로 이동을 위한 반복문 

str = litr.previous();

System.out.print(str + '\t');

if(str.equals("Robot"))                            // " Robot" 만나면 "Robot2" 저장

litr.add("Robot2");

}

. . . .

}







반응형