리스트(List)의 개념

배열은 데이터를 그룹핑해서 다수의 데이터를 효율적으로 관리하는 데이터 스트럭쳐입니다. 배열의 가장 큰 특징은 인덱스가 있다는 점이지요. 이 인덱스는 데이터를 매우 빠르게 가져옵니다. 하지만 인덱스를 써 데이터를 가져오려면 각 데이터의 인덱스 값이 고정되어야 합니다. 또 어떤 엘리먼트를 삭제하면 삭제된 데이터가 있던 자리는 비워둬야 해 메모리가 낭비됩니다. 또 배열에 데이터가 있는지 없는지 항상 확인해야 합니다.

리스트는 배열의 장점인 빠른 데이터 조회 대신, 빈틈없는 데이터 적재라는 장점을 취한 데이터 스트럭쳐입니다.

삭제

아래와 같은 데이터에서 4번째 엘리먼트인 40을 제외하면 어떻게 될까요?

img

배열이라면 아래와 같이 40의 자리가 빈자리로 남습니다. 덕분에 50의 인덱스 값은 변하지 않습니다. 이처럼 배열에서 인덱스는 주민등록 번호처럼 변하지 않는 고유한 값으로 남습니다.

img

반면 리스트의 경우, 데이터는 아래와 같이 저장됩니다.

img

데이터 밀도는 촘촘하게 유지됩니다. 하지만 50의 인덱스 값은 4에서 3으로 바뀌었습니다. 리스트는 인덱스를 식별자로 사용할 수 없습니다.

추가

데이터를 추가해볼까요? 아래와 같은 데이터가 있다고 가정합시다.

img

배열에서 인덱스를 유지하면서 40의 자리에 50을 추가하면 아래와 같이 되겠죠.

img

50이 삭제되었습니다. 이게 원했던 결과라면 상관없지만, 만약 30과 40의 사이에 50을 추가하고 싶었다면 아래와 같은 결과를 바랐을 겁니다. 이렇게 처리하려면 리스트를 써야 합니다.

img

리스트에서는 인덱스가 중요하지 않습니다. 리스트 데이터 스트럭쳐에선 엘리먼트 간의 순서가 핵심입니다. 그래서 리스트를 다른 말로는 시퀀스(sequence)라고 부르기도 합니다. 시퀀스는 순서라는 의미죠. 즉, 순서가 있는 데이터의 모임이 리스트입니다.

수학 유한수열(finite sequence)을 프로그래밍으로 표현한 게 리스트입니다. 지금 수열을 몰라도 됩니다. 리스트를 익히고 수학의 수열을 공부하면 재미있을 겁니다. 수학을 잘하면 프로그래밍하는 데 큰 도움이 됩니다. 하지만 프로그래밍을 해보고 수학을 공부하면 수학을 왜 공부해야 하는지 알 수 있습니다. 프로그래밍하기 전에 꼭 수학을 배워야 하진 않습니다. 그러니 수학에 무지하다고 겁먹지 마세요.

리스트의 기능

리스트의 핵심은 리스트가 순서가 있는 엘리먼트의 모임이라는 점입니다. 리스트는 빈 엘리먼트를 허용하지 않습니다. 또 리스트는 (배열과 마찬가지로) 중복된 데이터를 허용한다는 걸 기억해두세요. 어떤 데이터 스트럭쳐는 중복된 데이터를 허용하지 않는데요. 자세한 건 나중에 알려드리겠습니다.

리스트의 정의를 알아볼까요. 아래와 같은 기능이 있고, 순서가 있으면서 중복을 허용하는 데이터 스트럭쳐를 리스트라고 합니다.

  • 처음, 끝, 중간에 엘리먼트를 추가/삭제하는 기능
  • 리스트에 데이터가 있는지를 확인하는 기능
  • 리스트의 모든 데이터에 접근할 수 있는 기능

내장된 리스트

리스트를 직접 구현하기보다는 언어에 내장된 리스트를 사용하세요. 내장 리스트는 많은 사람이 검증해 안전하며 속도도 빠릅니다. 하지만 모든 언어가 리스트를 내장하진 않습니다. 또, 언어별로 리스트를 제공하는 방식이 다릅니다. 이제 몇 가지 언어를 비교하면서 리스트를 깊이 이해해봅시다.

꽤 오래된 언어인 C에는 배열은 있지만, 리스트는 없습니다. 리스트가 필요할 땐 직접 구현해서 사용하지요. 여러분이 C를 사용하는 개발자라면 리스트를 구현하는 방법은 필수로 알아야 합니다. 최근 등장한 언어의 창시자는 리스트의 중요성을 알고 있었습니다. 이들은 리스트를 기본으로 지원해 개발자의 고충을 덜어주자는 생각을 했습니다. 이제는 많은 언어가 다양한 방식으로 리스트를 지원합니다.

자바스크립트의 경우는 배열에 리스트의 기능을 포함하고 있습니다. 아래는 인덱스 3인 엘리먼트를 삭제하는 코드입니다.

numbers = [10, 20, 30, 40, 50];
//인덱스 3의 값을 제거
numbers.splice(3,1);
for(i = 0; i < numbers.length; i++){
    console.log(numbers[i]);
}

실행결과는 아래와 같습니다.

10
20
30
50

실행

위의 코드에서 아랫부분이 가장 중요합니다.

numbers.splice(3,1);

splice는 배열 student의 3번째 인덱스에 위치한 엘리먼트를 삭제합니다. 삭제한 엘리먼트가 있던 자리는 뒤의 엘리먼트가 채웁니다. 즉 자바스크립트의 배열은 리스트의 기능도 포함하고 있는 셈이지요. 각각의 엘리먼트가 고유한 인덱스를 갖게 하려면 splice 대신에 numbers[3] = null; 과 같은 방법을 사용하세요. 자바스크립트의 배열은 사용하는 방법에 따라 배열이 될 수도 있고 리스트가 될 수도 있습니다. 어떻게 쓸지는 여러분의 선택에 달렸습니다.

파이썬은 자바스크립트와 비슷한 방법으로 리스트를 지원합니다. 그런데 파이썬에는 배열은 없고 리스트만 있습니다. 기능은 자바스크립트의 배열과 거의 같지만 이름이 리스트이죠. 같은 코드를 파이썬으로 작성해봅시다. 실행

numbers = [10, 20, 30, 40, 50];
numbers.pop(3);
for number in numbers:
    print(number);

실행결과는 자바스크립트와 같습니다.

자바스크립트나 파이썬과 같은 언어를 스크립트 언어라고 부릅니다. 스크립트는 '쉽다'는 가치를 추구합니다. 리스트와 같이 개발자가 자주 사용하는 기능을 언어에 포함해, 쉽게 사용할 수 있도록 배려하지요.

사용하는 언어에 따라 리스트의 효용은 다릅니다. 특히 요즘은 많은 언어가 기본으로 리스트를 지원해, 개발자가 리스트를 직접 구현하는 일은 줄었습니다. 그러면 이제는 리스트를 공부할 필요가 없을까요? 결론부터 말하면 그렇지 않습니다. 파이썬과 비슷한 시기에 나온 언어, 자바를 통해서 그 이유를 생각해 봅시다.

자바에서의 리스트

자바의 경우 배열과 리스트를 모두, 따로 지원합니다. 아래는 자바에서 배열을 사용한 예시입니다.

int[] numbers = {10,20,30,40,50};

int[]은 변수 numbers가 정수를 엘리먼트로 담는 배열임을 의미합니다. 자바에서 배열의 원소를 제거하긴 쉽지 않습니다. 자바 배열은 자바스크립트의 splice나 파이썬의 pop 같은 기능을 제공하지 않기 때문입니다. 대신 자바에서는 리스트라는 데이터 스트럭쳐를 지원합니다.

ArrayList numbers = new ArrayList();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.add(50);
numbers.remove(3);
System.out.println(numbers);

실행

자세히 보시면 이름이 List가 아니라 ArrayList 입니다. 왜 이런 이름이 있을까요? 코드를 조금 바꿔보겠습니다. 실행

LinkedList numbers = new LinkedList();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.add(50);
numbers.remove(3);
System.out.println(numbers);

두 코드의 결과는 아래처럼 같습니다.

[10, 20, 30, 50]

두 코드는 어떻게 다를까요? 첫 줄에서 아래와 같이 다른 코드가 사용되고 있습니다.

img

ArrayList와 LinkedList는 둘 다 List라는 공통점이 있고, 사용법과 실행 결과가 비슷합니다. 하지만 구현 방법은 아주 다릅니다.

img

이는 LCD와 CRT 모니터의 동작법은 아주 다르지만, 컴퓨터 입장에서는 완전히 똑같은 모니터인 것과 비슷한 이치입니다.

자바는 개발자가 배열과 리스트 중에 자신의 상황에 맞는 선택을 분명하게 선택할 수 있도록 둘을 별도의 기능으로 지원하고 있습니다. 그렇다면 자바에서 하나의 리스트를 지원하지 않고 LinkedList와 ArrayList를 둘 다 지원하는 이유는 무엇일까요? 이를 이해하려면 LinkedList와 ArrayList의 구현방법을 이해해야 합니다. 결론을 말하자면 데이터를 빈번히 조회한다면 ArrayList가, 데이터의 추가/삭제가 빈번하다면 LinkedList가 훨씬 효과적입니다.

img

대규모 시스템을 구축하려면 상황에 맞는 적합한 데이터 스트럭쳐를 선택해야 합니다. 이런 판단을 하려면 직접 데이터 스트럭쳐를 구현해서 사용하지 않더라도 데이터 스트럭쳐의 내부 메커니즘을 이해해야 합니다.

무엇보다도 이후에 배울 다양한 데이터 스트럭쳐는 내부에서 리스트를 사용합니다. 따라서 리스트를 충분히 이해하면 다른 데이터 스트럭쳐를 이해하기 쉽습니다.

여기까지 리스트가 무엇인지를 설명했습니다. 후속 수업에선 ArrayList와 LinkedList를 구현하는 방법을 살펴보겠습니다.

강의에 등록된 질문이 없습니다. 궁금한 부분이 있으면 주저하지 말고 무엇이든 물어보세요.