swift & iOS/swift

[swift] 배열에 요소를 추가하는 append와 insert 그리고 딸기 케이크 (?)

whale3 2022. 2. 4. 12:59

스위프트의 배열에 어떤 값을 새로 추가하고 싶을 때는 append나 insert 메소드를 사용하면 된다. 

// 출처: https://developer.apple.com/documentation/swift/array

var students = ["Ben", "Ivy", "Jordell"]
students.append("Maxime")
students.append(contentsOf: ["Shakia", "William"])
students.insert("Liam", at: 3) // // ["Ben", "Ivy", "Jordell", "Liam", "Maxime", "Shakia", "William"]

append 메소드를 사용하면 배열의 맨 끝에 값을 추가한다. contentsOf를 사용하면 배열을 통채로 끝에 추가할 수도 있다. 배열의 중간에 값을 추가하려면 insert 메소드를 사용하여 원하는 위치에 값을 삽입할 수 있다. 

 

다만 insert 처럼 배열의 중간에 값을 추가하려면 약간 수고로운 부분이 있다. 스위프트의 배열은 아래 케이크 사진의 딸기처럼 메모리에 줄줄이 다닥 다닥 붙어서 자리를 차지하고 있다(contiguous block of memory). 그래서 딸기 하나를 케익 중간에 추가하고 싶으면 옆에 있는 딸기들을 한 칸씩 옆으로 옮긴 다음에 새로운 딸기가 들어갈 공간을 만들어야 딸기를 추가할 수 있다. 배열에서도 마찬가지로 insert 하려는 새로운 요소를 위해 기존 요소들을 하나씩 옮겨서 새로운 값이 들어갈 공간을 마련해야 한다. 반면에 append 처럼 배열의 맨 끝에 값을 추가하는 방법은 insert 보다 덜 번거롭다. 딸기를 맨 끝에 추가하고 싶으면 다른 딸기들을 옮길 필요 없이 그냥 맨 끝에 딱 놓으면 되는 것처럼 말이다. 

 

출처: https://unsplash.com/photos/cu1-a9LSmqo

 

 

그럼 딸기들을 열심히 추가하다가 케익에 자리가 모자라 더 이상 딸기를 놓을 수 없을 땐 어떻게 해야할까? 조금 더 큰 케익을 새로 만든 다음에 기존 케익에 있던 딸기들을 하나씩 옮겨야 한다. 공식 문서에 배열의 사이즈에 관해 설명해둔 부분을 보면, 배열은 값들을 담을 수 있도록 메모리에서 일정 공간을 차지하고 있는데 이 공간보다 넘치게 되면 배열은 메모리에다가 지금보다 2배 더 큰 공간을 차지한 다음, 새 공간으로 기존의 요소들을 복사한다고 나와 있다. 케익을 새로 만들면 시간이 걸리는 것처럼 메모리에서 조금 더 큰 공간을 다시 차지하고 기존 요소들을 복사하는 것도 시간이 걸리지만 2배, 4배, 8배 ... 로 계속 큰 케익을 만들다보면 딸기를 놓을 공간이 모자라서 더 큰 케익을 만들어야 하는 일은 점점 줄어들 것이다. 

 

출처: https://developer.apple.com/documentation/swift/array

Growing the Size of an Array
Every array reserves a specific amount of memory to hold its contents. When you add elements to an array and that array begins to exceed its reserved capacity, the array allocates a larger region of memory and copies its elements into the new storage. The new storage is a multiple of the old storage’s size. This exponential growth strategy means that appending an element happens in constant time, averaging the performance of many append operations. Append operations that trigger reallocation have a performance cost, but they occur less and less often as the array grows larger.

 

 

만약 내가 케익 위에 딸기를 몇 개나 놓을 것인지 미리 알 수 있다면 그에 맞는 케익을 만들면 자리가 모자라서 케익을 갑자기 더 만들어야 하는 일은 없을 것이다. 마찬가지로 배열 크기가 어느 정도여야 하는지 알 수 있다면 reserveCapacity 메소드를 사용하여 배열의 크기를 미리 정할 수 있고 capacity 프로퍼티로 배열에 요소를 얼마나 저장할 수 있는지 확인할 수 있다. 

 

 

If you know approximately how many elements you will need to store, use the reserveCapacity(_:) method before appending to the array to avoid intermediate reallocations. Use the capacity and count properties to determine how many more elements the array can store without allocating larger storage.

 

 

이렇게 배열의 중간에 값을 삽입하는 것은 좀 수고로운 작업이지만 (중간에 있는 값을 삭제하는 것도 마찬가지이다. 케익 중간에 있는 딸기를 빼면 그 자리를 다시 하나씩 옮겨서 채워야 한다.) 대신 원하는 값을 빠르게 얻을 수 있다는 장점이 있다. 케익 위의 세번째 딸기를 바로 집어서 먹을 수 있는 것처럼 배열의 인덱스를 알기만 하면 해당 인덱스에 있는 값을 빠르게 가져올 수 있다. 

 

 

스택 오버 플로우에 좋은 답변이 있어서 이것도 참고 하였다.

https://stackoverflow.com/questions/27943629/how-does-swift-manage-arrays-internally

 

How does Swift manage Arrays internally?

I would like to know how Swift managed arrays internally? Apple's language guide only handles usage, but does not elaborate on internal structures. As a Java-developer I am used to looking at "bare"

stackoverflow.com

 

반응형