[Swift] Array の反転操作でのあれこれ
概要
Swift で Array
を反転する方法として、以下の4パターンに分けて解説していきます。
- Iteration で用いたい場合
- 処理を加えた上で、Iteration を行いたい場合
- 新しい逆順の
Array
が欲しい場合 - in-place で反転させたい場合
解説
Array
を用いたい場合
Iteration で逆順の Iteration において逆順の Array
を用いたい場合には、型を明示的に指定せずに .reversed()
を利用することで、逆順に Array
内の要素にアクセスできます。このとき、.reversed()
を呼び出す際には、ReversedCollection
型が返され、ReversedCollection
型は元々の Array
への参照を保持しているため、メモリの再割り当てをせずに、各要素に逆順でアクセスできます。Array
自体をコピーする必要がないため、パフォーマンスの観点で優れています。
// 元の配列
let nums: [Int] = [1, 2, 3, 4]
for num in nums.reversed() {
print(num) // 4, 3, 2, 1 が順に出力
}
- 利点
- 新しいメモリを使用せずに、各要素に逆順にアクセスできるため、メモリ効率が良い
-
.reversed()
の時間・空間計算量はO(1)
- 欠点
- 逆順のアクセスは iteration 内での使用に限定される
処理を加えた上で、Iteration を行いたい場合
各要素に何らかの処理を施した上で、逆順にアクセスを行いたい場合は、.map
を用いることで、処理を加えた上でのアクセスが可能になります。また、lazy
を用いることによって、重い処理であっても、遅延で処理を行うこともできます。ただし、.map
を使用した場合、新しくメモリを使用することになるため、パフォーマンスの低下が起きます。
- 通常の
.map
の使用法
// 元の配列
let nums: [Int] = [1, 2, 3, 4]
let reversedMapped = nums.reversed().map { $0 * 2 }
for str in reversedMapped {
print(str) // [8, 6, 4, 2]
}
-
lazy
での.map
の使用法
// 元の配列
let nums: [Int] = [1, 2, 3, 4]
let lazyMapped = nums.lazy.reversed().map { $0 * 2 }
// lazyMapped は LazyMapCollection で、
// for-in などで使うときに初めて、必要なメモリが確保される。
for str in lazyMapped {
print(str) // [8, 6, 4, 2]
}
- 利点
- 処理を加えたうえで、逆順にアクセスできる
- lazyを使用すると、大規模な配列に対する複雑な処理を効率的に実行できる。
- 欠点
- 新しくメモリに割り当てる必要があるため、パフォーマンスが落ちる
-
.reversed().map()
の時間・空間計算量はO(n)
Array
が欲しい場合
新しい逆順の 新しい逆順の Array
が必要な場合には、明示的に型変換を行うことで、ReversedCollection
型ではなく、Array
型の変数を得ることができます。やはりメモリの再割り当てが発生するため、パフォーマンスは低下してしまいます。
// 元の配列
let nums: [Int] = [1, 2, 3, 4]
let reversedNums: [Int] = nums.reversed()
// もしくは
let reversedNums = Array(nums.reversed())
print(reversedNums) // [4, 3, 2, 1]
- 利点
- 元の配列とは独立した、新しい逆順の
Array
を作成できる。
- 元の配列とは独立した、新しい逆順の
- 欠点
- メモリの再割り当てが発生するため、パフォーマンスが低下する。
- 時間・空間計算量は
O(n)
in-place で反転させたい場合
in-place で反転させたい場合は、.reverse()
を用いることで、新たにメモリを確保することなく、Array
を反転させることができます。
// 元の配列
var nums: [Int] = [1, 2, 3, 4]
nums.reverse()
print(nums) // [4, 3, 2, 1]
- 利点
- メモリ効率が良い。(
O(1)
)
- メモリ効率が良い。(
- 欠点
- 元の配列のデータが変更される
- 時間計算量は、
O(n)
まとめ
Swift で Array を反転させる際のパターンを4つ紹介しました。単純に各要素を逆順にアクセスしたい場合には、そのまま.reversed()
を使用するなど、状況に合わせて各手法を使い分けるのが良いでしょう。
各手法の比較
手法 | 利点 | 欠点 | 時間計算量 | 空間計算量 |
---|---|---|---|---|
.reversed() |
メモリ効率が良い、新しい Array を作成しない | iteration 内での使用に限定 | O(1) | O(1) |
.reversed().map() |
処理を加えた上で反転できる | メモリ効率が悪い | O(n) | O(n) |
Array(nums.reversed()) |
新しい逆順の Array を作成できる | メモリ効率が悪い | O(n) | O(n) |
.reverse() |
メモリ効率が良い、パフォーマンスが良い | 元の配列の内容が変更される | O(n) | O(1) |
参照
Discussion