コンテンツへスキップ
0%

Solidityの配列とは?

Alchemy team headshot

執筆者 Alchemy

2023年9月22日 公開読了時間 1 分

配列は、同じデータ型の要素を固定サイズで格納する線形データ構造であり、要素は隣接した連続するメモリ位置に格納される。配列内の各要素は、その位置、つまりインデックスによって指定される。

配列は0からn-1までのインデックスベースのシステムで動作する。ここでnは配列の全体サイズを表す。同じ型の変数を個別に複数作成する代わりに、開発者は任意のサイズの配列を1つ宣言するだけで、インデックスを使って要素にアクセスできる。

Solidityは、EthereumおよびEVM互換ブロックチェーンで使用される主要なweb3プログラミング言語であり、配列は固定サイズまたは動的サイズのいずれかを取ることができる。オブジェクト指向プログラミング言語として、Solidityはスマートコントラクト間の継承をサポートしており、複数のコントラクトを1つのコントラクトに継承させることができる。

Solidityの配列についてさらに深く学びたい場合は、Alchemy Universityの無料Solidity開発者コースに登録してほしい。

動的配列と固定配列とは

動的配列は宣言時にサイズが事前に定義されないのに対し、固定配列は事前にサイズが定義されている。 要素が体系的に追加されるにつれて動的配列のサイズは変化し、実行時に配列の実際のサイズが決定される。

対照的に、固定配列は事前に定義されたサイズを持ち、配列内に存在する要素の数はその配列のサイズを超えてはならない。配列のサイズが指定されない稀なケースでは、「十分なサイズ」の配列が作成される。「十分なサイズ」とは、初期化を格納するのに「十分」なサイズを意味する。

ストレージ配列 vs. メモリ配列

Solidityでは、開発者が考慮すべき配列には2種類ある:ストレージ配列とメモリ配列だ。

ストレージ配列

ストレージ配列は通常、状態変数として宣言され、固定サイズまたは動的サイズのいずれかを取ることができる。動的な長さを持つストレージ配列はサイズ変更が可能であり、これはpush()とpop()の両関数にアクセスできることを意味する。これらの関数はそれぞれ、配列への要素の追加と削除を可能にする点に注意が必要だ。

メモリ配列

メモリ配列はデータ位置としてmemoryを指定して宣言される。ストレージ配列と同様に、メモリ配列もコンパイル時に固定または動的な長さを持つことができるが、メモリが割り当てられた後にサイズを変更することはできない。つまり、pop()とpull()のSolidity関数は使用できない。

固定サイズのメモリ配列は、uint256[5] memory numbersのような宣言をした時点で自動的に割り当てられる。しかし、動的なメモリ配列を割り当てるには、new演算子を使用する必要がある。例えば、uint256[] memory numbers = new uint256[](5)``;のように使用できる。

注: 配列を使用する前に必ず初期化し、有効なアドレスを取得できるようにしておくこと。

Solidityの配列とmappingの比較

Solidityにおけるmappingは、データのグループを格納するための参照型という点で配列と似ている。しかし、mappingの構文と構造は配列とはかなり異なり、これによって独自の重要な役割を果たすことができる。mappingはキーと値のテーブルであり、それぞれが事前に定義された型を持つ。mappingは、Solidityスマートコントラクト内に存在する空のテーブルを作成し、データで満たされるのを待っている状態として考えることができる。

配列とは異なり、mappingには取得可能な長さがなく、キーや値を初期化段階で「設定」する必要もない。さらに、Solidityの配列でできるようなループ処理をmappingに対して行うことはできない。

しかし、mappingからデータを取得する方が、配列から同じデータを取得するよりもはるかに効率的だ。配列からデータを取得するには、目的の要素が見つかるまで配列全体を反復処理する必要があるが、mappingであればそのデータを即座に取得できる。

mappingからのデータ取得によるパフォーマンス上の利点は、Ethereumスマートコントラクトでガスを節約する上で非常に重要になり得る。ブロックチェーン上のデータを編集するトランザクションにはガス代がかかるためだ。したがって、スマートコントラクトへのデータの格納や取得をできるだけ効率的に行うことで、開発者は長期的にETHを節約できる。

まとめると、forループなどを使ってデータのグループを反復処理する必要がある場合は配列を使用する。データセットを反復処理する必要がなく、既知のキーに基づいて値を取得できる場合は、mappingの使用を検討する。

配列の宣言と初期化

Solidityで配列を宣言するのは比較的簡単だ。要素のデータ型と要素の数を指定する必要があり、配列のサイズは正の整数であるべきで、データ型はSolidityで有効なものでなければならない。

例えば、以下の基本的な命令は配列を初期化し、データが挿入された後にconsole.logに表示される:

配列内の各項目は要素と呼ばれ、各要素は「n - 1」という関数を通じてその数値インデックスでアクセスできる。ここでnは要素番号を表す。

一般的に、要素に関連する番号付けは0から始まる。つまり、10番目の要素はインデックス9でアクセスされ、11番目の要素はインデックス10でアクセスされる、という具合だ。

同じデータ型に関する制約を覚えておくことは重要だ。なぜなら、配列は連続したメモリセルに格納されるため、すべてのセルが同じ型、したがって同じサイズでなければならないからだ。

構造体(Structs)

構造体は、プログラマーが独自のデータ型を定義できるようにする。構造体が定義されると、状態変数として、あるいは位置引数またはキーワードを使用する他の多数の関数の中で使用できる。後者のアプローチでは、構造体に含まれるメンバーの順序を覚えておく必要がなくなる。

以下は新しい構造体の例だ:

Mapping

Mappingは、すべての可能なキーが存在し、任意の値を1回の操作でキーを使って設定または取得できるキーと値のストアと考えることができる。KeyTypeは、bytes、string、あるいは任意のcontract/enum型など、任意の組み込み値型を使用できる。ValueTypeは、mapping、配列、構造体を含む任意の型を使用できる。

Mappingは以下のように宣言できる:

注:mapping変数に許可されるデータ位置はstorageのみであることに注意。

メンバー

配列内の特定のメンバーを見つけるには、そのメンバーが見つかるまで配列を検索する必要がある。開発者は2つの重要な関数を知っておくべきだ:lengthpush

  • Length - 配列のサイズを返し、動的配列のサイズを変更するためにも使用できる。
  • Push - 動的なストレージ配列の末尾に直接要素を追加でき、結果として配列の新しい長さを返す。

Solidity配列のトラブルシューティング

開発者は時折コンパイルエラーに遭遇することがあり、これは配列の宣言、作成、初期化に関する規則の誤解から生じることがある。以下は、Solidity配列に関する一般的なエラーをトラブルシューティングするための高レベルな方法だ:

1. Array.length

array.length関数を使用すると、配列内に存在する要素の数を確認できる。メモリ配列のサイズは宣言時に固定されるが、動的配列は実行時に定義されるため、操作にはlengthが必要になる。

2. Abi.encodeとabi.encodePacked

外部コントラクトに対して関数が呼び出されると、EVMは関数シグネチャと引数を含むバイトバッファを計算する。引数をシリアライズする方法には2つある:abi.encodeabi.encodePacked

Abi.encodeは、ABI仕様を使用してパラメータをエンコードする。実質的に、ABIはコントラクトへの呼び出しを行うために設計されており、パラメータは32バイトにパディングされる。コントラクトへの呼び出しが行われる場合、この関数が使用されている可能性が高い。

対照的に、abi.encodePackedは、その型が必要とする最小限のスペースを使用してパラメータをエンコードする。unit8をエンコードする場合は1バイトを使用し、スペースを節約したい場合やコントラクトを呼び出す意図がない場合に使用される。

3. Byte[]またはbytes

両方の配列型は、任意の長さの生のバイトデータを保持できる。byte\[\] とbytesの違いは、bytes\[\]が配列型の規則に従う点であり、Solidityのメモリ配列内の要素は常に32バイトの倍数を占める。つまり、要素が32バイトの倍数より少ない場合、必要なサイズに達するまでパディングされる。例えば、byte配列の場合、各要素につき最大31バイトが無駄になる可能性があり、これはbytes配列やstringを使用する場合には発生しない。

4. String

この特定の動的配列はUTF-8データ型であり、他のプログラミング言語と機能が異なる。Solidityは、文字列の長さを取得したり、2つの文字列の連結や比較を行う関数を提供していない。文字列はbytes[]を使用して迅速にバイト配列に変換できる。

5. Solidity配列のnull

Python、JavaScriptなど他のプログラミング言語には_undefined_、nullnil、_none_といった概念が存在するが、Solidityにはこれらは存在しない。代わりに、開発者はこれをゼロまたはデフォルト値の概念と呼ぶことができる。なぜなら、値が作成されると必ずメモリ内にスロットが割り当てられ、したがって何かを含んでいるはずだからだ。

Solidity配列についてさらに学ぶ方法

Solidityプログラミング言語についてさらに学び、配列を含む他の基礎的なトピックに慣れるには、Alchemy UniversityのEthereumブートキャンプをチェックしてほしい。Ethereumブートキャンプは、ガイド付き動画と実践的なプロジェクトを完備した無料の7週間のプログラミングコースだ。

開発全般に不慣れな開発者にとっては、Ethereumブートキャンプを始める前の良い前提条件として、AlchemyのEthereum向けJavaScript 3週間コースがある。

Background gradient

ブロックチェーンで魔法を生み出す

Alchemyは、最も強力なweb3開発者向けプロダクトとツールを、豊富なリソース、コミュニティ、そして卓越したサポートと組み合わせて提供します。