배열

양주영·2021년 8월 24일
0

javascript

목록 보기
6/42

객체는 순서를 고려하지 않고 만든 자료구조이기 때문에 객체를 이용하면 새로운 프로퍼티를 기존 프로퍼티 ‘사이에’ 끼워 넣는 것도 불가능하다.
때문에, 순서가 있는 컬렉션이 필요한데 이럴 때 배열을 사용할 수 있다.

배열이란?


배열(array)은 같은 타입의 변수들로 이루어진 유한 집합이다.
배열을 구성하는 각각의 값을 배열 요소(element)라고 하고, 배열에서의 위치를 가리키는 숫자는 인덱스(index)라고 한다.



배열 선언

let arr = new Array();
let arr = [];

대부분 두 번째 방법으로 배열을 선언하는데, 이 때 대괄호 안에 초기 요소를 넣어주는 것도 가능하다.

let fruits = ["사과", "오렌지", "자두"];

  • 배열 내 항목의 인덱스 찾기
let fruits = ["사과", "오렌지", "자두"];

alert( fruits[0] ); // 사과
alert( fruits[1] ); // 오렌지
alert( fruits[2] ); // 자두

  • 배열 수정 및 추가하기
fruits[2] = '배'; // 배열이 ["사과", "오렌지", "배"]로 바뀜
fruits[3] = '레몬'; // 배열이 ["사과", "오렌지", "배", "레몬"]으로 바뀜

  • 배열에 담긴 요소가 몇 개인지 확인하기
let fruits = ["사과", "오렌지", "자두"];

alert( fruits.length ); // 3

  • 배열 요소의 자료형에는 제약이 없다.
// 요소에 여러 가지 자료형이 섞여 있습니다.
let arr = [ '사과', { name: '이보라' }, true, function() { alert('안녕하세요.'); } ];

// 인덱스가 1인 요소(객체)의 name 프로퍼티를 출력합니다.
alert( arr[1].name ); // 이보라

// 인덱스가 3인 요소(함수)를 실행합니다.
arr[3](); // 안녕하세요.


Pop push 와 shift unshift


큐의 구조는 스택과 다르게 '선입선출'의 구조를 가지고 있습니다. 스택은 막힌 박스와 같아 먼저 들어온 것이 가장 나중에 나가는 구조지만, 큐는 먼저 들어온 것이 먼저 나가는 구조입니다. 

  • 스택 : 후입선출 (Last-In-First-Out)
  • : 선입선출 (First-In-First-Out)

큐(queue)는 배열을 사용해 만들 수 있는 대표적인 자료구조로, 배열과 마찬가지로 순서가 있는 컬렉션을 저장하는데 사용한다. 큐에서 사용하는 주요 연산은 아래와 같다.

  • push : 맨 끝에 요소를 추가한다.
  • shift : 제일 앞 요소를 꺼내 제거한 후 남아있는 요소들을 앞으로 밀어준다. 이렇게 하면 두 번째 요소가 첫 번째 요소가 된다.

배열은 큐 이외에 스택(stack)이라 불리는 자료구조를 구현할 때도 쓰인다.
스택에서 사용하는 연산은 아래와 같다.

  • push : 요소를 스택 끝에 집어넣는다.
  • pop : 스택 끝 요소를 추출한다.

스택은 이처럼 '한쪽 끝'에 요소를 더하거나 뺄 수 있게 해주는 자료구조이다.
이렇게 처음이나 끝에 요소를 더하거나 빼주는 연산을 제공하는 자료구조를 컴퓨터 과학 분야에서는 데큐(deque, Double Ended Queue)라고 부른다.

아래는 배열 끝에 무언가를 해주는 메서드이다.

  • pop
    배열 끝 요소를 제거하고, 제거한 요소를 반환한다.
let fruits = ["사과", "오렌지", "배"];

alert( fruits.pop() ); // 배열에서 "배"를 제거하고 제거된 요소를 얼럿창에 띄웁니다.

alert( fruits ); // 사과,오렌지
  • push
    배열 끝에 요소를 추가한다.
let fruits = ["사과", "오렌지"];

fruits.push("배");

alert( fruits ); // 사과,오렌지,배

fruits.push(...)를 호출하는 것은 fruits[fruits.length] = ... 하는 것과 같은 효과를 보인다.

아래는 배열 앞에 무언가를 해주는 메서드이다.

  • shift
    배열 앞 요소를 제거하고, 제거한 요소를 반환한다.
let fruits = ["사과", "오렌지", "배"];

alert( fruits.shift() ); // 배열에서 "사과"를 제거하고 제거된 요소를 얼럿창에 띄웁니다.

alert( fruits ); // 오렌지,배
  • unshift
    배열 앞에 요소를 추가한다.
let fruits = ["오렌지", "배"];

fruits.unshift('사과');

alert( fruits ); // 사과,오렌지,배

push와 unshift는 요소 여러 개를 한 번에 더해줄 수도 있다.
let fruits = ["사과"];

fruits.push("오렌지", "배");
fruits.unshift("파인애플", "레몬");

// ["파인애플", "레몬", "사과", "오렌지", "배"]
alert( fruits );


배열의 내부 동작 원리


배열은 특별한 종류의 객체이다. 다만 배열은 키가 숫자라는 점만 다르다.
숫자형 키를 사용함으로써 배열은 객체 기본 기능 이외에도 순서가 있는 컬렉션을 제어하게 해주는 특별한 메서드를 제공한다. length라는 프로프터디도 제공한다.
하지만, 어쨌든 배열의 본질은 객체이다.
배열은 자바스크립트의 일곱 가지 원시 자료형에 해당하지 않고, 원시 자료형이 아닌 객체형에 속하기 때문에 객체처럼 동작한다.

let fruits = []; // 빈 배열을 하나 만듭니다.

fruits[99999] = 5; // 배열의 길이보다 훨씬 큰 숫자를 사용해 프로퍼티를 만듭니다.

fruits.age = 25; // 임의의 이름을 사용해 프로퍼티를 만듭니다.

배열은 객체이므로 예시처럼 원하는 프로퍼티를 추가해도 문제가 없다.

잘못된 방법의 예는 다음과 같다.

  • arr.test = 5 같이 숫자가 아닌 값을 프로퍼티 키로 사용하는 경우
  • arr[0]arr[1000]만 추가하고 그 사이에 아무런 요소도 없는 경우
  • arr[1000], arr[999] 같이 요소를 역순으로 채우는 경우

배열은 순서가 있는 자료를 저장하는 용도로 만들어진 특수한 자료구조이다.



성능


결론부터 말하자면, pushpop은 빠르지만 shiftunshift는 느리다.

그 이유는 인덱스가 0인 요소를 제거뿐 아니라 나머지 요소들을 수정해줘야 하기 때문이다.

shift연산은 아래 3가지 동작을 모두 수행해야 이뤄진다.
1. 인덱스가 0인 요소를 제거한다.
2. 모든 요소를 왼쪽으로 이동시킨다. 이때 인덱스 1은 0, 2는 1로 변한다.
3. length 프로퍼티 값을 갱신한다.

배열에 요소가 많으면 요소가 이동하는 데 걸리는 시간이 길고 메모리 관련 연산도 많아진다.


pop메서드는 요소를 옮기지 않으므로 각 요소는 기존 인덱스를 그대로 유지한다. 이것이 배열 끝에 무언가를 해주는 메서드의 실행 속도가 빠른 이유다.



반복문


for문은 배열을 순회할 때 쓰는 가장 오래된 방법이다. 순회 시엔 인덱스를 사용한다.

let arr = ["사과", "오렌지", "배"];

for (let i = 0; i < arr.length; i++) {
  alert( arr[i] );
}

배열에 적용할 수 있는 또 다른 순회 문법으론 for..of가 있습니다.

let fruits = ["사과", "오렌지", "자두"];

// 배열 요소를 대상으로 반복 작업을 수행합니다.
for (let fruit of fruits) {
  alert( fruit );
}

for..of를 사용하면 현재 요소의 인덱스는 얻을 수 없고 값만 얻을 수 있다. 원하는 것을 충분히 구현할 수 있고 문법도 짧기 때문에 배열의 요소를 대상으로 반복 작업을 할 땐 for..of를 사용해 보자.

배열은 객체형에 속하므로 for..in을 사용하는 것도 가능하다.

let arr = ["사과", "오렌지", "배"];

for (let key in arr) {
  alert( arr[key] ); // 사과, 오렌지, 배
}

그런데 for..in은 다음과 같은 특징을 지니기 때문에 배열에 for..in을 사용하면 문제가 발생하므로 되도록 다른 반복문을 사용하자.

  1. for..in 반복문은 모든 프로퍼티를 대상으로 순회한다. 키가 숫자가 아닌 프로퍼티도 순회 대상에 포함된다.

브라우저나 기타 호스트 환경에서 쓰이는 객체 중, 배열과 유사한 형태를 보이는 ‘유사 배열(array-like)’ 객체가 있다. 유사 배열 객체엔 배열처럼 length 프로퍼티도 있고 요소마다 인덱스도 붙어 있다. 그런데 여기에 더하여 유사 배열 객체엔 배열과는 달리 키가 숫자형이 아닌 프로퍼티와 메서드가 있을 수 있다. 유사 배열 객체와 for..in을 함께 사용하면 이 모든 것을 대상으로 순회가 이뤄지기 때문에 ‘필요 없는’ 프로퍼티들이 문제를 일으킬 가능성이 생긴다.

  1. for..in 반복문은 배열이 아니라 객체와 함께 사용할 때 최적화되어 있어서 배열에 사용하면 객체에 사용하는 것 대비 10~100배 정도 느리다.

그러니 배열엔 되도록 for..in를 쓰지 말자.



‘length’ 프로퍼티


length 프로퍼티는 배열 내 요소의 개수가 아니라 가장 큰 인덱스에 1을 더한 값이다.

따라서 배열에 요소가 하나 있고, 이 요소의 인덱스가 아주 큰 정수라면 배열의 length 프로퍼티도 아주 커진다.

let fruits = [];
fruits[123] = "사과";

alert( fruits.length ); // 124


new Array()


new Array() 문법을 사용해도 배열을 만들 수 있다.

let arr = new Array("사과", "배", "기타");

대괄호 []를 사용하면 더 짧은 문법으로 배열을 만들 수 있기 때문에 new Array()는 잘 사용되지 않는 편이다.

숫자형 인수 하나를 넣어서 new Array를 호출하면 배열이 만들어지는데, 이 배열엔 요소가 없는 반면 길이는 인수와 같아진다.

예시를 통해 new Array()의 이런 특징이 어떻게 실수를 유발할 수 있는지 알아보자.

let arr = new Array(2); // 이렇게 하면 배열 [2]가 만들어질까요?

alert( arr[0] ); // undefined가 출력됩니다. 요소가 하나도 없는 배열이 만들어졌네요.

alert( arr.length ); // 길이는 2입니다.


다차원 배열


배열 역시 배열의 요소가 될 수 있다.
이런 배열을 다차원 배열(multidimensional array)라고 부른다. 행렬을 저장하는 용도로 쓰인다.

let matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

alert( matrix[1][1] ); // 5, 중심에 있는 요소


toString


배열엔 toString 메서드가 구현되어 있어 이를 호출하면 요소를 쉼표로 구분한 문자열이 반환된다.

let arr = [1, 2, 3];

alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true

alert( [] + 1 ); // "1"
alert( [1] + 1 ); // "11"
alert( [1,2] + 1 ); // "1,21"

배열에는 Symbol.toPrimitive나 valueOf 메서드가 없다. 따라서 위 예시에선 문자열로의 형 변환이 일어나 []는 빈 문자열, [1]은 문자열 "1", [1,2]는 문자열 "1,2"로 변환된다.



정리


  • 배열은 특수한 형태의 객체로, 순서가 있는 자료를 저장하고 관리하는 용도에 최적하된 자료구조이다.

  • 선언 방법 :

// 대괄호 (가장 많이 쓰이는 방법임)
let arr = [item1, item2...];

// new Array (잘 쓰이지 않음)
let arr = new Array(item1, item2...);
  • length 프로퍼티는 배열의 길이를 나타내준다. 정확히는 숫자형 인덱스 중 가장 큰 값에 1을 더한 값이다. 배열 메서드는 length 프로퍼티를 자동으로 조정해준다.

  • length 값을 수동으로 줄이면 배열 끝이 잘린다.

  • push(...items) : items를 배열 끝에 더해준다.

  • pop() : 배열 끝 요소를 제거하고, 제거한 요소를 반환해준다.

  • shift() : 배열 처음 요소를 제거하고, 제거한 요소를 반환한다.

  • unshift(...items) : items를 배열 처음에 더해준다.

  • 아래 방법을 사용하면 모든 요소를 대상으로 반복 작업이 가능하다.
    - for (let i=0; i<arr.length; i++) : 가장 빠른 방법이고 오래된 브라우저와도 호환됩니다.
    - for (let item of arr) : 배열 요소에만 사용되는 모던한 문법입니다.
    - for (let i in arr) : 배열엔 절대 사용하지 마세요.



참조 : https://ko.javascript.info/array#ref-5154

profile
뚜벅뚜벅

0개의 댓글