Swiftの配列(Arrays)

配列(Arrays)

配列(Arrays)です。プログラミングではお馴染みの、同じ型のデータを系統的に処理したい場合に用いられる箱みたいなものです。使いこなせると便利ですが、私も昔c++でプログラミングをしていた際は、配列で一番実行エラーを起こした記憶があります。

配列(Arrays)とは?

Swiftでの配列(Arrays)は、

同じ型を持つ値を順番に格納するためのコレクション型

です。例えば、あるwebサイトにおける1日のアクセス数を、ページ毎に数えるための変数を用意したい場合を考えます。仮にページ数が3ページだとすると、Int型変数が3個必要です。

// 普通の変数でアクセスカウンター
var access0 = 10 // 1ページ目
var access1 = 7  // 2ページ目
var access2 = 11 // 3ページ目

ページ数が少ない場合は、このままで良いかもしれません。しかしページ数が増える毎に変数を増やすのは面倒です。

ここで使えるのが配列です。上記のケースを配列で置き換えると、例えば

// アクセスカウンターを配列で表現
var access: [Int] = [10, 7, 11]

と書けます。このように、配列を使うと同じ型を持つ値をまとめて表現できますし、後述するように拡張(この例だとページ数を増やすこと)も容易です。

配列の初期化

先ずは配列の初期化です。以下の例では全て変数として宣言しています。これは、後から配列の要素を増やすことを想定しているからです。ここで「配列の要素」とは、配列に入っている個々の値のことです。

配列リテラル(array literal)で初期化

配列リテラルとは

// 配列リテラル
[value1, value2, value3]

といった具合に、角括弧[ ]で囲んだ値をコンマ,で区切ったものです。

実際の初期化例を、Intの場合で見てみましょう。

// 配列の初期化
var arrayIntEx0: Array<Int> = [10, 7, 11] // (1)
var arrayIntEx1: [Int] = [10, 7, 11] // (2)
var arrayIntEx2: Array = [10, 7, 11] // (3)
var arrayIntEx3 = [10, 7, 11] // (4)

4つ初期化方法を書きましたが、結果は全て同じです。

(1)Array<type>はSwiftの文法的に最も正しい書き方ですが、毎回書くには面倒です。(2)の書き方[type]は(1)のショートカットです(公式マニュアルではこの方法を推奨しています)。(3)Arrayは型を省略した初期化方法で、右辺の値の型推論からこのような初期化も可能です。さらに、(4)のように全部省略した省エネ初期化も可能です。

もちろん、DoubleStringの配列も作成可能です。

// Double, String配列の初期化
var arrayDouble = [3.14, 10.5, 3.14]
var arrayString = ["hello", "world", "!"]

Doubleの例では3.14という値が2回使われていますが、Arrayの要素は重複可能ですので問題ありません。


補足:配列は「同じ型を持つ値を入れる箱じゃないのか?」という点に関して、ブログ記事にしましたので、興味のある方は参考にして下さい。

Swiftで配列に違う型を入れられる件(Swift 2.x系とSwift 3以降では挙動に違いがあるので追記しました)


空の配列宣言、代入

配列は要素なし(空の配列)で初期化することも可能です。このような初期化の場合には、必ず型を指定する必要があります。

// 空の配列
var someInt = [Int]()    // 公式マニュアルに記載の初期化
var emptyInt: [Int] = [] // こういう初期化もできる
print("配列の要素数: (someInt,emptyInt)=(\(someInt.count),\(emptyInt.count))")
// "配列の要素数: (someInt,emptyInt)=(0,0)"と表示

2通りの初期化方法を書きましたが、どちらも同じです。Array.countというのは、まだ馴染みのない表現ですが、countは配列の要素数を表す変数になります。このように、型が保有している変数や定数のことをプロパティ(properties)と呼びます。今、空の配列を作ったので、要素数は0です。したがってcount0を返します。

試しに、ひとつ前の例で作ったarrayStringの要素数を数えると、3であることが分かります。

// 要素数を数える
print("arrayStringの要素数は\(arrayString.count)です")
// "arrayStringの要素数は3です"と表示

また、すでに変数の型が確定している場合には、空の配列リテラル[]を使用して、既存の配列を空にすることもできます。

// 空の配列を代入
arrayString = []
print("arrayStringの要素数は\(arrayString.count)です")
// "arrayStringの要素数は0です"と表示

arrayStringStringであることが分かってますので、改めてStringを指定する必要はありません。


補足:
Swift 2.x系では、型指定なしの配列リテラルでも初期化できます。ただし、この場合Object-Cの型NSArrayになるようで、Swiftの文法では取り扱えませんので注意です。

// 型指定なしで空の配列リテラル
var emptyArray = [] // NSArray型になる
emptyArray.append(3) // コンパイルエラー

Swift 3以降では、型指定無しで空の配列リテラルを使って初期化することは出来ません。

var emptyArray = []
//error: empty collection literal requires an explicit type

別の言い方をすると、空の配列で初期化する場合、必ず型を指定する必要があります。


配列の初期化子を使って初期化

配列に限らずSwiftの全ての型には初期化子(initializer)という特別な機能があって、これを使って配列を生成することが可能です。

C言語が分かる人にはお馴染みかもしれませんが、Swiftの初期化子はC言語で言う所のコンストラクタです。

ここでは初期化子については触れませんが、配列の場合

// 初期化子を使った配列の初期化
var arrayDoubleRepeat = Array(repeating: 1.0, count: 5)
// arrayDoubleRepeatの型は[Double]、配列要素は全て1.0なので、
// [1.0, 1.0, 1.0, 1.0, 1.0]と等価

このように配列を作ることが可能です。

最初のrepeatingが配列要素を指定し、2番目のcountが配列要素の数を指定していることは理解できると思います。上記サンプルコードでは型の明示的指定がどこにもありませんが、配列要素の値が1.0であることから、[Double]に型推論されます。

このような書式になる意味を理解するためには、構造体、引数(パラメータ)、初期化子等々についての知識が必要ですので、ここでは詳細な説明は割愛します。

別の配列を組み合わせて新しい配列を作る

新しい配列を作る時に既存の配列を利用することも可能です。試しにやってみると、

// 既存の配列を使って新しい配列を生成
var combinedArray = arrayDouble + arrayDoubleRepeat
//[3.14, 10.5, 3.14, 1.0, 1.0, 1.0, 1.0, 1.0]

このようになります。ここでは明示的にcombinedArrayの型が指定されていませんが、arrayDoublearrayDoubleRepeatは両方とも[Double]ですから、combinedArray[Double]に型推論されます。

また、上記の例のように既存の配列を組み合わせる場合は、その型が一致していない場合はコンパイルエラーになります。

// [Double]と[Int]ではコンパイルエラー
var combinedArray = arrayDouble + arrayInt
//error: type of expression is ambiguous without more context

これは異なる型の四則演算がコンパイルエラーになるのと同様です。

配列要素へのアクセス、配列要素の変更

配列の要素にアクセスしたり、要素を変更する方法は色々あります。公式マニュアルには様々な方法が解説してありますが、ここではsubscript syntax(日本語にすると添字構文でしょうか?)を中心に紹介します。他の方法は、構造体(Structures)などを学習した後の方が理解が深まりますので、ここでは簡単な紹介に留めたいと思います。

Subscript自体に関しては、「Swiftをもっと深く学ぶ」の「サブスクリプト(Subscripts)」にまとめましたので、詳細はそちらを参照下さい。

Subscript syntax

例えば、subscript syntaxを使って、配列の1つ目の要素にアクセスしたい場合は

// 配列要素へのアクセス
var someString: [String] = ["Morning", "午後"]
print("1つ目の要素 = \(someString[0])")
// "1つ目の要素 = Morning"と表示

と書きます。Subscript syntax(ここでは[0])は、基本的には定数や変数の直後に(スペースなしで)書きます。ここで注意しないといけないのは、要素を指定する添字は0から始まるということです。

また、存在しない要素にはアクセスできません。

var string = someString[2]
// 実行エラー

既存の要素を変更することも可能です(Arrayが変数の場合)。

// 配列要素の変更
someString[1] = "Afternoon"
print("1つ目の要素 = \(someString[0]), 2つ目の要素 = \(someString[1])")
// "1つ目の要素 = Morning, 2つ目の要素 = Afternoon"と表示 

配列要素を追加する場合には、複合代入演算子の+=が使えます。

// 配列要素の追加
someString += ["Evening"] // 0:"Morning", 1:"Afternoon", 2:"Evening"
someString += ["午前", "hello", "world", "!"] // まとめて追加もできる
// 配列要素数は7個

さらに、範囲演算子を使えば、配列のある特定範囲の要素をまとめて変更することもできます。

// 範囲演算子でまとめて変更
someString[4...6] = ["午後", "夜"]

基本的には指定した範囲(ここでは4...6)の要素を、新しく代入した要素(ここでは["午後", "夜"])で置換します。今、指定した範囲は要素4, 5, 6ですが、新しい要素は2つしかありません。この場合、新しく代入した要素は4, 5に入りますので、既存の要素6(["!"])は消去されます。

for-in文を使って、配列要素をまとめて表示すると

// 配列要素の表示(for-in文)
for element in someString {
  print(element)
}
// 表示結果
// Morning
// Afternoon
// Evening
// 午前
// 午後
// 夜

確かに、元々あった["!"]がなくなっています。

Properties & Methodによるアクセス

プロパティ(properties)メソッド(methods)に関してはここでは割愛しますが、公式マニュアルにも記載のある方法も簡単に紹介します。

// 要素数へアクセス
someString.count
// 要素数が空であるかのチェック
// "someString.count == 0"のショートカット
if someString.isEmpty {
  print("someStringは空です")
}
// 配列の最後尾に要素を追加
someString.append("hello")
// 特定の配列要素を置換
//someString.insert("world", atIndex:0) // Swift 2.xでの書式
someString.insert("world", at: 0) // Swift 3以降
// 特定の配列要素を消去
//someString.removeAtIndex(1) // Swift 2.xでの書式
someString.remove(at: 1) // Swift 3以降
// 最後の配列要素を消去
someString.removeLast()

Swift 2から3へのアップデートで変更のあったメソッドについては併記しておきました。比較するとSwift 3ではメソッド名が簡略化され、元々メソッド名に含まれてた前置詞などがパラメータ名になっていたりします。

まとめ

  • 配列(Arrays)は、基本的に、同じ型を持った値を順番にまとめるコレクション型
  • 配列リテラル[value1, value2, ...]、または空の配列で初期化できる
  • Subscript syntaxを使って個々の配列要素にアクセス・変更ができる
  • また、配列の持つプロパティ(properties)、メソッド(methods)を使ったアクセス・変更も可能