前のページ次のページ上に戻るホーム SophiaFramework UNIVERSE 5.3

13.2. 配列

SFXArray クラスは、 複数の要素を配列として連続したメモリ領域(内部バッファ)に格納し、 インデックスを使用して要素にダイレクトアクセスするためのデータ構造です。

[Note] SFXArray と SFXList

SFXArray クラスでは、 要素の参照はインデックスを使用してダイレクトにアクセスできます。 一方、SFXList クラスでは、 要素の参照はリンクを辿る必要があります。 そのため、要素の参照は SFXList クラスよりも高速に行えます。

SFXArray クラスでは、 要素の挿入や削除は当該要素以降の要素を内部バッファメモリ内で移動する必要があります。 一方、SFXList クラスでは、 要素の挿入と削除は、前後の要素と当該要素のポインタの付け替え操作だけです。 そのため、要素の挿入や削除は SFXList クラスよりも低速に行われます。

デフォルトの設定では、 最初に要素を追加したときに SFXArray::SetThreshold 関数で設定するサイズ(デフォルトでは 4 要素分)の内部バッファがまとめて確保されます。 その後、順次要素を追加していったとき、 要素を格納する領域が不足する場合は、 SFXArray::SetCluster 関数で設定するクラスタサイズ単位(デフォルトでは 8 要素分)で内部バッファは拡張されます。 逆に、要素が削除されたときは、クラスタサイズ単位でまとめてメモリ再割り当てが実行され、メモリが解放されます。 因みに、1 要素のサイズは 4 バイトです。

内部バッファの拡張は、クラスタサイズ分だけ大きくした連続したメモリ領域が新たに確保され、 そこに既存の全要素がコピーされます。 頻繁にメモリ再割り当てを行うと、パフォーマンス劣化の一因になります。

予め要素数が予測できる場合は、 SFXArray::SetSize 関数を使用して要素数を設定する、 もしくは、SFXArray::SetThreshold / SFXArray::SetCluster 関数を使用して内部バッファの設定を最適化することを推奨します。

[Note] 注意
SFXArray::SetSize 関数を実行した後、 新たに追加された要素の値は 0 で初期化されます(既存の要素の値はそのままです)。

以下は、SInt32 型のデータを要素に持つ配列を定義するコードです。

// SInt32 型要素を格納するための配列の定義
SFXArray<SInt32> array;

// 現在の配列の状態: array = () 
[Note] 注意

要素のサイズは 4 バイト以下でなければいけません。

以下のコードでは、 SFXArray::InsertLast 関数を使用して配列に 4 つの要素を追加しています。

// 現在の配列の状態: array = () 

array.InsertLast(3);    // 4 要素分の内部バッファが確保される
// 現在の配列の状態: array = (3) 

array.InsertLast(-15);  
// 現在の配列の状態: array = (3, -15) 

array.InsertLast(0);    
// 現在の配列の状態: array = (3, -15, 0) 

array.InsertLast(1003); 
// 現在の配列の状態: array = (3, -15, 0, 1003) 
[Note] 注意

最初に SFXArray::InsertLast 関数を呼び出したときに、 SFXArray::SetThreshold 関数で設定するサイズ(デフォルトでは 4 要素分)の内部バッファがまとめて確保されます。

インデックスを使用して配列の要素を取得・設定するコードは、以下の通りです。

// 現在の配列の状態: array = (3, -15, 0, 1003) 

SInt32 n = array[1]; // n = -15
// 現在の配列の状態: array = (3, -15, 0, 1003) 

array[2] = 6;
// 現在の配列の状態: array = (3, -15, 6, 1003) 
[Note] 注意

リストの場合、 インデックスを使用して要素を取得・設定することはできません。

先頭・末尾の要素は SFXArray::GetFirst / SFXArray::GetLast 関数を使用して取得することも可能です。

// 現在の配列の状態: array = (3, -15, 6, 1003) 

// 先頭の要素を取得する
SInt32 first = array.GetFirst(); // first = 3

// 末尾の要素を取得する
SInt32 last  = array.GetLast();  // last  = 1003

配列のサイズ(要素数)を取得するには、 SFXArray::GetSize 関数を呼び出します。

// 配列のサイズ(要素数)を取得する
SInt32 n = array.GetSize(); // n = 4

配列に要素を挿入するには、SFXArray::Insert 関数を呼び出します。

// 配列に要素を挿入する

// 2 番目の要素の後ろに 99 を挿入する
// 5 番目の要素なので、クラスタサイズ(デフォルトでは 8 要素分)だけ大きくした内部バッファが新たに確保される
array.Insert(2, 99);
// 現在の配列の状態: array = (3, -15, 99, 6, 1003) 

// 先頭に -100 を挿入する
array.InsertFirst(-100);
// 現在の配列の状態: array = (-100, 3, -15, 99, 6, 1003) 
[Caution] 注意

配列に要素を挿入する場合は、以下の理由からパフォーマンス劣化に注意する必要があります。

  • 当該要素以降のすべての要素を移動します。
  • 内部バッファが満杯の場合、メモリの再割り当てを行います。

リストの場合、要素単位でヒープメモリが確保され、 当該要素と前後の要素のポインタ付け替えの操作を行うだけなので、要素の挿入は非常に高速です。

配列の要素を削除するには、 SFXArray::Remove 関数を呼び出します。

// 現在の配列の状態: array = (-100, 3, -15, 99, 6, 1003) 

// array[3] から array[4] までの要素を削除する
array.Remove(3, 5); 

// 現在の配列の状態: array = (-100, 3, -15, 1003) 
[Note] 注意

要素がポインタ型の場合、 要素を削除してもポインタの指すヒープメモリ領域は解放されません。 メモリリークの原因となるので注意が必要です。

配列のすべての要素を削除するには、 SFXArray::Clear 関数を呼び出します。 この関数は、内部バッファを解放します。

// 現在の配列の状態: array = (-100, 3, -15, 1003) 

// 配列のすべての要素を削除する
array.Clear(); 

// 現在の配列の状態: array = ()
// 内部のバッファは解放される

配列のサイズを設定するには、 SFXArray::SetSize 関数を呼び出します。 引数に配列の現在のサイズよりも大きな値を設定すると、 配列は拡張され、追加された各要素は 0 に設定されます。 既存の要素の値はそのままです。 小さな値を設定した場合は、 配列は縮小され、指定したインデックス以降の要素はすべて削除されます。

[Tip] Tip

指定したインデックスの要素の値を変更するには、 SFXArray::operator[] オペレータ、 もしくは、SFXArray::Set 関数を利用します。

// 現在の配列の状態: array = ()

// array のサイズを 5 に設定する: 各要素は 0 で初期設定される
array.SetSize(5); 

// 現在の配列の状態: array = (0, 0, 0, 0, 0)

// array の各要素の値を変更する
SInt32 i;
for (i = 0; i < array.GetSize(); ++i) {
    array[i] = 10 * (i - 2) * (i - 2);
}
// 現在の配列の状態: array = (40, 10, 0, 10, 40)

// array のサイズを 10 に設定する
array.SetSize(10); 
// 現在の配列の状態: array = (40, 10, 0, 10, 40, 0, 0, 0, 0, 0)
// 拡張された要素は 0 で初期設定される。既存要素の値はそのまま
[Note] 注意

予め要素の数が予測できる場合は、 SFXArray::SetSize 関数で適切なサイズを設定することでメモリ再割り当てによるパフォーマンス劣化を回避できます。 また、SFXArray::Insert / SFXArray::InsertFirst / SFXArray::InsertLast よりもインデックスを利用して要素の値を設定する方が効率的です。

以下は、引数に指定した値を持つ要素を検索する、またはそれが存在するか判定するコードです。 SFXArray::FirstIndexOf / SFXArray::LastIndexOf 関数は、 引数に指定した値を持つ要素を検索し、そのインデックスを取得します。 SFXArray::Contains 関数は、 引数に指定した値を持つ要素を含むか否か判定します。

// 現在の配列の状態: array = (40, 10, 0, 10, 40, 0, 0, 0, 0, 0)

SInt32 x;
Bool   b;

// 10 の値を持つ要素の最初のインデックスを検索する
x = array.FirstIndexOf(10);     // x = 1

// 5 の値を持つ要素の最初のインデックスを検索する
x = array.FirstIndexOf(5);      // x = -1: 当該要素が存在しないので -1 が返る

// 10 の値を持つ要素の最後のインデックスを検索する
x = array.LastIndexOf(10);      // x = 3

// 40 の値を持つ要素が存在するか判定する
b = array.Contains(40);         // b = true

// 100 の値を持つ要素が存在するか判定する
b = array.Contains(100);        // b = false

配列の要素を巡回するには、 反復子・列挙子またはインデックスを利用します。

[Note] 注意

リストの場合は、 インデックスを利用して要素にアクセスすることはできません。

例 13.1. インデックスによる巡回

SInt32 i;

for (i = 0; i < array.GetSize(); ++i) {

    SInt32 n = array[i]; // n に要素が入る

    //  n に対して処理をする

}
[Note] 注意
array[i] はインライン展開されて、内部バッファ内の i 番目の要素になります。

上記コードは列挙子や反復子を利用して以下のように記述することも可能ですが、 インデックスを利用する上記コードの方がパフォーマンス面で優れています。 なお、反復子を利用する場合は、要素を追加したり、削除することができます。

例 13.2. 列挙子による巡回

SFXArray<SInt32>::Enumerator etor = array.GetFirstEnumerator();

while(etor.HasNext()) {

    SInt32 n = etor.GetNext(); // n に要素が入る

    // n に対して処理をする

}

例 13.3. 反復子による巡回

SFXArray<SInt32>::Iterator itor = array.GetFirstIterator();

while(itor.HasNext()) {

    SInt32 n = itor.GetNext(); // n に要素が入る

    // n に対して処理をする

}
[Note] 注意
"etor.GetNext()" や "itor.GetNext()" は関数呼び出しなので、 インデックスを利用した配列要素へのダイレクトアクセスよりも計算コストが掛かります。