[Java] 컬렉션 프레임워크 List와 의존 관계 주입

자바의 컬렉션 프레임워크는 자료 구조를 다루기 위한 다양한 인터페이스의 모음이다. List, Set, Queue와 같은 인터페이스로 구성되어 있다. 이번 글에서는 컬렉션 프레임워크의 List 인터페이스, 하위 클래스인 ArrayList, LinkedList에 대해 살펴보고자 한다.

Java.util.List

List는 순서가 있고 중복을 허용하는 자료 구조이다. 배열도 마찬가지로 순서가 있고 중복을 허용한다. 그러나 자바에서 배열을 사용할 때 배열의 크기를 미리 선언해야 한다. 선언 후 크기를 변경 할 수 없고 고정된 크기를 가진다. 이와 달리 List는 크기를 동적을 변할 수 있다.

출처: 김영한의 실전 자바 - 중급 2편 자료

 

List 인터페이스에는 ArrayList와 LinkedList와 같은 구현 클래스가 있다. 위 그림을 살펴보면 ArrayList와 LinkedList가 List에 의존하고 있음을 알 수 있다. 주로 ArrayList를 사용할 때 아래와 같이 코드를 작성한다. 

List<Integer> numbers = new ArrayList<>();

여기서 List<Integer>는 인터페이스, ArrayList<>는 그 구현체를 의미한다. 이렇게 추상화하여 List 인터페이스를 사용하는 이유는 무엇일까? 의존관계 주입(Dependency Injection) 개념을 정리하며 더 깊이 이해해보자.

 

의존 관계 주입(Dependency Injection, DI)

의존 관계 주입은 객체가 다른 객체에 의존하지 않고 외부에서 필요한 객체를 주입 받도록 하는 설계 패턴을 말한다.

ArrayList를 활용해서 많은 데이터를 처리하는 BatchProcessor 클래스를 개발하고 있다고 가정한다. 개발하고 보니 데이터를 앞에 추가하는 일이 더 많아 LinkedList를 사용하는 것이 효율적이라고 판단되어 LinkedList로 바꾸려고 한다. 이 때 외부에서 의존관계 주입이 있는 경우와 없는 경우를 비교해보자.

 

 1. 의존 관계 주입이 없는 경우

public class BatchProcessor {

	private final ArrayList<Integer> list = new ArrayList<>(); //코드 변경
    
    public void logic(int size) {
    	for (int i = 0; i < size; i++) {
        	list.add(0, i);
            }
        }

}

구현체인 ArrayList에 의존하는 경우 BatchProcessor의 내부 코드를 변경해야 한다.

 

2. 의존 관계 주입이 있는 경우

public class BatchProcessor {

     private final List<Integer> list;
     
     public BatchProcessor(List<Integer> list) {
         this.list = list;
	}
    
     public void logic(int size) {
         for (int i = 0; i < size; i++) {
			list.add(0, i); //앞에 추가
          }
	}
}
main() {
new BatchProcessor(new ArrayList()); //ArrayList를 사용하고 싶을 때 
new BatchProcessor(new LinkedList()); //LinkedList를 사용하고 싶을 때
}

List 인터페이스에 의존하는 경우에는 BatchProcessor를 생성하는 시점에 생성자를 통해 선택해서 전달할 수 있다. 클라이언트 코드인 BatchProcessor를 변경하지 않고도 자료 구조를 변경할 수 있다.

 

컴파일 타임 vs 런타임 의존 관계

컴파일 타임과 런타임 의존 관계 개념을 통해 의존성 주입에 대해 더 쉽게 이해 할 수 있다. 

컴파일 의존 관계는 소스 코드 자체만 보고 나타나는 정적인 의존 관계이다.

컴파일 타임 의존 관계, 출처: 김영한의 실전 자바 - 중급 2편 자료

위의 도식을 보면 코드에서는 ArrayList나 LinkedList의 정보를 찾아 볼 수 없다. List에만 의존하고 있다.

런타임 의존 관계, 출처: 김영한의 실전 자바 - 중급 2편 자료

이와 달리 런타임 의존 관계는 프로그램이 실행되고 있는 상태에서 인스턴스 간의 의존 관계를 말한다. 프로그램 실행 중에 계속 변할 수 있다. List의 의존관계를 클래스에서 미리 결정하는 것이 아니라 런타임에 객체를 생성하는 시점으로 미룬다. 런타임에 구현체를 변경해도 클라이언트의 코드는 변경하지 않아도 되고 인터페이스 구현을 자유롭게 확장 할 수 있다. 

 

참고

김영한의 실전 자바 - 중급 1