관리 메뉴

보리차

chapter 23 컬렉션 프레임워크 1(1) 본문

Java

chapter 23 컬렉션 프레임워크 1(1)

보리콩 2022. 2. 23. 19:54

컬렉션 프레임워크

'프레임워크'란

자바에서 말하는 프레임워크란 "잘 정의된 구조의 클래스들"이라고 이해할 수 있다. 하지만 이를 '컬렉션 라이브러리'라 하지 않고 '컬렉션 프레임워크'라 하는 이유는 컬렉션 관련된 클래스의 정의에 적용되는 설계 원칙 또는 구조가 존재하기 때문이다.

 

컬렉션의 의미와 자료구조

컴퓨터 분야에는 '자료구조(Data Structures)'와 알고리즘(Algorithms)'이라는 학문이 있다. 자료구조에서 정형화하고 있는 데이터의 저장 방식 중 대표적인 몇가지는 다음과 같다.

  리스트(List), 스택(Stack), 큐(Queue), 트리(Tree), 해쉬(Hash)

 

위 자료구조들을 대상으로 하는 비교적 간단한 알고리즘 몇 가지는 다음과 같다.

  버블 정렬(Bubble Sort), 퀵 정렬(Quick Sort), 이진 탐색(Binary Search)

 

컬렉션 프레임워크는 데이터의 저장 방법, 그리고 이와 관련있는 알고리즘에 대한 프레임워크이다. 더 쉽게 표현하자면 위에서 언급한 자료구조와 알고리즘을 제네릭 기반의 클래스와 메소드로 미리 구현해 놓은 결과물이다.

 

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

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

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

이 둘은 기능적 측면에서 완전히 동일하지만 인스턴스를 저장하는 방식에 차이가 있어 이로인한 장단점이 존재한다. 그리고 List<E>를 구현하는 컬렉션 클래스들이 갖는 공통적 특성이 있는데 다음과 같다.

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

 

ArrayList<E> 클래스

import java.util.List;
import java.util.ArrayList;

class ArrayListCollection {
	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.szie(); i++)
        	System.out.print(list.get(i) + '\t');
            
        list.remove(0);	// 첫 번째 인스턴스 삭제
    }
}

예제에서 ArrayList<E> 인스턴스의 생성문은 다음과 같다.

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

ArrayList<E>형 참조변수가 아닌 List<E>형 참조변수를 선언한 이유는 코드에 유연성을 제공하기 위함이다. 주로 List<E>에 선언된 메소드를 호출하기 때문에 굳이 ArrayList<E>형 참조변수를 선언할 필요가 없으며, 컬렉션 클래스의 교체도 용이해진다.

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

위 예제에서 인스턴스의 저장, 참조, 삭제의 방법을 봤는데, 실제로 컬렉션 프레임워크의 핵심은 이 세 가지이다. 그리고 컬렉션 인스턴스를 사용하면 배열처럼 길이를 신경쓰지 않아도 된다. ArrayList<E>인스턴스는 내부적으로 배열을 생성해서 인스턴스를 저장하는데, 필요하면 그 배열의 길이를 스스로 늘리기 때문이다. 단 배열의 길이를 늘린다는 것은 더 긴 배열로의 교체를 의미하므로 성능에 신경을 써야 한다면 ArrayList<E>의 다음 생성자를 알아 둘 필요가 있다.

public ArrayList(int initialCapacity) // 인자로 전달된 수의 인스턴스를 저장할 수 있는 공간을 미리 확보

 

LinkedList<E> 클래스

import java.util.List;
import java.util.LinkedList;

class ArrayListCollection {
	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.szie(); i++)
        	System.out.print(list.get(i) + '\t');
            
        list.remove(0);	
    }
}

LinkedList<E> 는 '연결 리스트(Linked List)'라는 자료구조를 기반으로 디자인된 클래스이다. 저장 공간을 열차 칸 추가하듯 늘릴 수 있기 때문에 ArrayList<E>와 달리 인스턴스의 저장 공간을 미리 마련해 둘 필요가 없다.

 

ArrayList<E> vs. LinkedList<E>

ArrayList<E>의 단점

  • 저장 공간을 늘리는 과정에서 시간이 비교적 많이 소요됨
  • 인스턴스의 삭제 과정에서 많은 연산이 필요할 수 있다. 따라서 느릴 수 있다.

ArrayList의 장점

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

LinkedList<E>의 단점

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

LinkedList의 장점

  • 저장 공간을 늘리는 과정이 간단하다.
  • 저장된 인스턴스의 삭제 과정이 단순하다.

 

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

저장된 모든 인스턴스들을 대상으로 하는 연산이 필요한 경우, 다음과 같이 for-each문을 사용할 수 있다.

for(String s : list)
	System.out.print(s + '\t');

위와 같이 for-each문을 통한 순차적 접근의 대상이 되려면, 해당 컬렉션 클래스는 다음 인터페이스를 구현해야 한다.

public interface Iterable<T>

앞서 소개한 ArrayList<E>, LinkedList<E> 클래스는 위의 인터페이스를 구현하고 있다. 정확히는 Collection<E>가 Iterable<T>를 상속하는데, ArrayLeist<E>, LinkedList<E>클래스는 Collection<E> 인터페이스를 구현한다.

public interface Colletion<E> extends Iterable<E>

 

 

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

Collection<E>를 구현하는 자바의 제네릭 클래스는 Iterable<T>의 다음 추상 메소드를 모두 구현한다.

Iterator<T> iterator

이 메소드는 '반복자(Iterator)'라는 것을 반환한다. 반복자는 저장된 인스턴스들을 순차적으로 참조할 때 사용하는 인스턴스다.

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

반복자를 통해 호출할 수 있는, Iterator<E>의 메소드들은 다음과 같다.

E next()		다음 인스턴스의 참조 값을 반환
boolean hasNext()	next 메소드 호출 시 참조 값 반환 가능 여부 확인
void remove()		next 메소드 호출을 통해 반환했던 인스턴스 삭제

위 메소드를 활용한 코드는 다음과 같다.

// 반복자를 이용한 참조 과정 중 인스턴스의 삭제
while(itr.hasNext()) {		// next 메소드가 반환할 대상이 있다면,
    str = itr.next();		// next 메소드를 호출
    if(str.quals("Box"))
    	itr.remove();		// 위에서 next 메소드가 반환한 인스턴스 삭제
}

이러한 반복자는 생성과 동시에 첫 번째 인스턴스를 가리키고, next가 호출될 때마다 가리키는 대상이 다음 인스턴스로 옮겨진다. 그렇다면 이 반복자를 원하는 때에 다시 첫 번째 인스턴스를 가리키게 하려면 다시 반복자를 얻어야 한다.

 

앞서 소개한 for-each문도 반복자를 이용하는 코드로 수정할 수 있다.

for(Iterator<String> itr = list.iterator(); itr.hasNext();)
	System.out.print(itr.next() + '\t');

 

 

컬렉션 변환

배열과 ArrayList<E>는 특성이 유사하지만 대부분의 경우 배열보다 ArrayList<E>가 저장과 삭제가 편하고 반복자를 쓸 수 있다는 점에서 더 좋다. 단 배열처럼 '선언과 동시에 초기화'를 할 수 없는 점이 조금 번거롭지만 다음과 같이 컬렉션 인스턴스를 생성할 수 있다.

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

그러나 이렇게 생성된 컬렉션 인스턴스는 새로운 인스턴스의 추가나 삭제가 불가능하므로 인스턴스의 추가나 삭제가 필요하다면 다음 생성자를 기반으로 ArrayList<E> 인스턴스를 생성해야 한다.

class ArrayList<E> {
    public ArrayList(Collection<? extends E> c) {...}	// 생성자
    ...
}

public ArrayList(Collection<? extends E> c)

  • Collection<E>를 구현한 컬렉션 인스턴스를 인자로 전달받는다.
  • 그리고 E는 인스턴스 생성 과정에서 결정되므로 무엇이든 될 수 있다.
  • 덧붙여서 매개변수 c로 전달된 컬렉션 인스턴스에서는 참조만 가능하다.
// List<E>는 collection<E>를 상속한다.
List<String> list - Arrays.asList("Toy", "Box", "Robot");

// 생성자 public ArrayList(Collection<? extends E> c)를 통한 인스턴스 생성
list = new ArrayList<>(list);

위와 같이 ArrayList<E> 인스턴스를 생성하면, 생성자로 전달된 컬렉션 인스턴스에 저장된 모든 데이터가 새로 생성되는 ArrayList<E> 인스턴스에 복사된다.

 

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

public ArrayList(Collection<? extends E> c)
public LinkedList(Collection<? extends E> c)
public HashSet(Collection<? extends E> c)

 

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

컬렉션 인스턴스도 기본 자료형의 값은 저장하지 못한다. 따라서 래퍼 클래스를 통해 값의 저장 및 참조가 가능하다.

 

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

List<E>를 구현하는 클래스의 인스턴스들만 얻을 수 있는 '양방향 반복자'가 있다.

public ListIterator<E> listIterator()

위의 메소드가 반환하는 반복자는 양쪽 방향으로 이동이 가능하다는 특징이 있는데, 이는 배열이나 연결 리스트와 같은 자료구조의 특성상 가능한 일이다.

E next()		다음 인스턴스의 참조 값을 반환
boolean hasNext()	next 메소드 호출 시 참조 값 반환 가능 여부 확인
void remove()		next 메소드 호출을 통해 반환했던 인스턴스 삭제

E previous()		
boolean hasPrevious()	

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

 

'Java' 카테고리의 다른 글

chapter 24 컬렉션 프레임워크 2  (0) 2022.02.28
chapter 23 컬렉션 프레임워크1(2)  (0) 2022.02.23
chapter 22 제네릭(Generics) 2  (0) 2022.02.16
chapter 21 제네릭(Generics) 1  (0) 2022.02.15
chapter 20 자바의 기본 클래스  (0) 2022.02.10