C# PR

【C#】ListとArrayListの違いとは?特徴と使い分け方

【C#】ListとArrayListの違いとは?特徴と使い分け方
記事内に商品プロモーションを含む場合があります

こんにちは!
C#でプログラミングをしていると、リストを扱う場面がよくあります。
C#には、リストを扱うための代表的なクラスとしてListArrayListがあるんです。
でも、この2つのクラスの違いや使い分け方がよくわからないという方も多いのではないでしょうか。

初心者の方にとっては、「どっちを使えばいいの?」と迷ってしまうこともあるかもしれません。
そこで、この記事ではListArrayListの違いを詳しく解説していきます。
それぞれの特徴やメリット・デメリットを理解して、適材適所で使い分けられるようになりましょう。

この記事を読めば、以下のようなことがわかるようになります。

この記事でわかること
  • ListArrayListの基本的な違い
  • それぞれの使い方と具体的なサンプルコード
  • パフォーマンスの比較と使い分けのポイント
  • 他のデータ構造との比較

プログラミング初心者の方から中級者の方まで、幅広い方に役立つ内容になっているので、ぜひ最後までお付き合いください。

ListとArrayListの基本的な特徴

まずは、ListArrayListそれぞれの基本的な特徴を見ていきましょう。

Listの特徴

Listは、ジェネリックのコレクションクラスです。
つまり、格納する要素の型を指定することができるんですね。
これにより、型の安全性が高まり、パフォーマンスも向上します。

また、Listは配列をベースにしているので、要素へのアクセスが高速というメリットがあります。
ただし、要素の挿入や削除には時間がかかるというデメリットも。

ArrayListの特徴

一方、ArrayListは、型を指定せずにオブジェクトを格納できる非ジェネリックのコレクションクラスです。
どんな型でも受け入れてくれる柔軟性が特徴ですが、代わりに型の安全性は低くなります。
要素にアクセスする際にはボックス化・アンボックス化のオーバーヘッドが発生するので、パフォーマンスもListに劣ります。

ListとArrayListの主な違い

主な違いは以下の2点だと言えるでしょう。

  1. 型の指定: Listはジェネリックで型を指定できる。ArrayListは型を指定しない。
  2. パフォーマンス: Listの方が要素へのアクセスが高速。ArrayListはボックス化・アンボックス化のオーバーヘッドがある。

基本的には、Listを使うのがおすすめです。
型の安全性とパフォーマンスのメリットを享受できますからね。
ただ、型が不明な場合や、異なる型を混在させたい場合はArrayListを選ぶことになるでしょう。

ボックス化とは、値型のデータをオブジェクト型に変換することを指します。
反対に、オブジェクト型から値型に変換することをアンボックス化と言います。

C#では、int、double、boolなどの値型は、スタック上に直接値を格納します。
一方、オブジェクト型のデータは、ヒープ上に格納されます。
ArrayListは、様々な型のオブジェクトを格納できますが、値型をそのまま格納することはできません。

例えば、intの値をArrayListに追加する際は、いったんIntオブジェクトにボックス化されます。
つまり、スタック上の値がヒープ上のオブジェクトにコピーされるわけです。
このボックス化の処理には、メモリの割り当てとコピーのコストがかかります。

そして、ArrayListから値を取り出す際は、アンボックス化が行われます。
Intオブジェクトから整数値を取り出し、スタック上にコピーするんですね。
こちらの処理にもコストがかかります。

こうしたボックス化・アンボックス化のオーバーヘッドが、ArrayListのパフォーマンスを下げる原因の一つになっています。
一方、Listはジェネリックなので、値型をそのまま格納できます。
ボックス化・アンボックス化が不要なので、パフォーマンスが良いというわけですね。

ただし、値型をオブジェクト型として扱いたい場合は、ボックス化が必要になります。
そういった場面では、ArrayListが活躍します。
ボックス化のコストを理解した上で、適材適所で使い分けていくのがポイントですよ。

ListとArrayListの使い方

では、ListArrayListの基本的な使い方を見ていきましょう。

Listの使い方

まずは、Listの使用例からですね。

Listを使うには、まず以下のようにインスタンスを生成します。


// stringのListを生成
var stringList = new List<string>();

ここで、List<string>のように、ジェネリック型パラメータで要素の型を指定します。
これが、Listの強みの1つですね。

生成したら、Addメソッドで要素を追加できます。


// 要素を追加
stringList.Add("apple");
stringList.Add("cherry");
stringList.Add("banana");

Removeメソッドを使えば、要素の削除も簡単。


// 要素を削除
stringList.Remove("banana");

要素へのアクセスは、インデックスを使って行います。


// インデックスを指定して要素にアクセス
var element = stringList[0];

ArrayListの使い方

一方、ArrayListの使い方は、以下のような感じです。


// ArrayListを生成
var arrayList = new ArrayList();

// 要素を追加
arrayList.Add("dog");
arrayList.Add(123);
arrayList.Add(true);

型の指定がないので、文字列や数値、真偽値など、様々な型の要素を追加できるのが特徴ですね。

要素の削除やアクセスは、Listと同様の方法で行えます。


// 要素を削除
arrayList.Remove("dog");

// インデックスを指定して要素にアクセス
var element = arrayList[0];

ただし、要素にアクセスする際は、object型として扱われるので、適宜キャストが必要になります。

以上が、ListArrayListの基本的な使い方になります。
サンプルコードを実際に動かしてみて、使い方を体感してみてくださいね。

パフォーマンスの違い

ListArrayListのパフォーマンスを比較してみましょう。
まず、要素へのアクセスは、Listの方が高速です。
Listは、要素の型が指定されているので、ボックス化・アンボックス化のオーバーヘッドがありません。
一方、ArrayListは、要素にアクセスする際にボックス化・アンボックス化が発生するため、パフォーマンスが低下します。

次に、要素の追加と削除のパフォーマンスを見てみましょう。
Listは、要素の追加や削除の際に、内部配列のサイズ調整が発生する場合があります。
これによって、パフォーマンスが低下することがあるんです。
一方、ArrayListは、初期サイズを指定できるので、サイズ調整のオーバーヘッドを最小限に抑えられます。

以下は、ListArrayListのパフォーマンスを比較する簡単なサンプルコードです。


using System.Collections;
using System.Diagnostics;

var stopwatch = new Stopwatch();
var listSize = 1000000;

// Listのパフォーマンス測定
var intList = new List<int>();
stopwatch.Start();
for (var i = 0; i < listSize; i++)
{
	intList.Add(i);
}
stopwatch.Stop();
Console.WriteLine($"List: {stopwatch.ElapsedMilliseconds}ms");

// ArrayListのパフォーマンス測定
var intArrayList = new ArrayList();
stopwatch.Restart();
for (var i = 0; i < listSize; i++)
{
	intArrayList.Add(i);
}
stopwatch.Stop();
Console.WriteLine($"ArrayList: {stopwatch.ElapsedMilliseconds}ms");
結果

List: 5ms
ArrayList: 61ms

この例では、100万個の要素をListArrayListに追加するのにかかる時間を測定しています。
実行してみると、Listの方がArrayListよりも高速であることがわかります。

ただし、これはあくまで一例であって、状況によってパフォーマンスは変わります。
要素の型や数、操作の種類などによって、パフォーマンスに差が出ることもあるでしょう。

一般的には、要素の型が明確で、パフォーマンスが重要な場合はListを使うのがおすすめです。
型の安全性とパフォーマンスのメリットを活かせますからね。
一方、異なる型の要素を扱う必要があったり、パフォーマンスよりも柔軟性が求められる場合は、ArrayListを選ぶことになるでしょう。

使い分けのポイント

ここまで、ListArrayListの特徴やパフォーマンスの違いを見てきました。
では、実際にどのように使い分ければいいのでしょうか。

基本的には、要素の型が明確で、パフォーマンスが重要な場合は、Listを使うのがおすすめです。
例えば、以下のようなケースですね。

  • 大量のデータを扱うアプリケーション
  • リアルタイム性が求められるゲームやシミュレーション
  • 科学計算や金融計算など、高速な数値計算が必要な場面

これらの場合、Listの型安全性とパフォーマンスのメリットを最大限に活用できます。

一方、柔軟性が必要で、異なる型の要素を扱う必要がある場合は、ArrayListを選ぶことになります。
例えば、以下のような場面ですね。

  • 様々な型のオブジェクトを保持するコンテナクラス
  • プラグインアーキテクチャで、動的に異なる型のオブジェクトを扱う場合
  • シリアライズ・デシリアライズで、型情報を保持する必要がある場合

こうした場合は、ArrayListの柔軟性を活かすことができるでしょう。

他のデータ構造との比較

リストといえば、ListArrayListが代表的ですが、他にも様々なデータ構造があります。
ここでは、それらと比較しながら、ListArrayListの特徴を再確認しましょう。

HashSet

まずは、HashSetです。
HashSetは、重複する要素を許容しない集合を表すデータ構造です。
要素の追加や削除、検索が高速に行えるので、重複チェックが必要な場面で活躍します。

ただし、要素の順序は保証されません。
順序が重要な場合は、ListArrayListを使う必要がありますね。


// HashSetの例
var hashSet = new HashSet<int>();
hashSet.Add(1);
hashSet.Add(2);
hashSet.Add(2); // 重複は無視される

Dictionary

次に、Dictionaryを見てみましょう。
Dictionaryは、キーと値のペアを保持する連想配列です。
キーを使って値を高速に検索できるので、データの関連付けや、キーによるデータの管理に適しています。

ただし、キーの重複は許容されません。
重複を許容する場合は、ListArrayListを使いましょう。


// Dictionaryの例
var dictionary = new Dictionary<string, int>();
dictionary["apple"] = 1;
dictionary["banana"] = 2;
var value = dictionary["apple"];

StackとQueue

また、スタックとキューも重要なデータ構造です。
スタックは、Last-In-First-Out(後入れ先出し)の動作を提供します。
一方、キューは、First-In-First-Out(先入れ先出し)の動作を提供します。
これらは、特定の順序で要素を処理する際に便利ですね。


// スタックの例
var stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
var top = stack.Pop(); // 2が取り出される

// キューの例
var queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
var first = queue.Dequeue(); // 1が取り出される

データの特性や処理の要件に応じて、適切なデータ構造を選ぶことが大切です。
ListArrayListだけでなく、他のデータ構造の特徴も理解して、使い分けていきましょう。

その他ListとArrayListの便利な使い方

ListArrayListを効果的に活用するための使い方をいくつか紹介します。

初期容量の設定

リストの要素数が予測できる場合は、コンストラクタで初期容量を指定しましょう。
これにより、内部配列の再割り当てが減り、パフォーマンスが向上します。


var list = new List<int>(1000); // 初期容量を1000に設定
var arrayList = new ArrayList(1000);

型の明示的な指定

Listを使う際は、できるだけ型を明示的に指定しましょう。
これにより、型の安全性が高まり、パフォーマンスも向上します。


var intList = new List<int>(); // 整数のリスト
var stringList = new List<string>(); // 文字列のリスト

メモリ使用量の最適化

不要になった要素は速やかに削除し、TrimExcessメソッドを使ってメモリを解放しましょう。


list.RemoveAt(0); // 先頭の要素を削除
list.TrimExcess(); // 余分なキャパシティを削除

LINQの活用

ListはLINQと相性が良いので、複雑な操作を行う際はLINQを活用しましょう。


var evenNumbers = intList.Where(x => x % 2 == 0).ToList();

スレッドセーフな使用

複数のスレッドからアクセスする場合は、Listの代わりにConcurrentBagの使用を検討しましょう。


var concurrentBag = new ConcurrentBag<int>();

カスタムクラスの活用

複雑なデータ構造を扱う場合は、カスタムクラスを作成し、それをリストの要素として使用することを検討しましょう。


var personList = new List<Person>();
personList.Add(new Person { Name = "Alice", Age = 30 });

キャリア形成/給与還元
ひとつひとつ真摯に向き合う企業
ONE_WEDGE社員募集

株式会社 ONE WEDGEでは、新たな仲間を募集しています!

私たちと一緒に、革新的で充実したキャリアを築きませんか?
当社は、従業員が仕事と私生活のバランスを大切にできるよう、充実した福利厚生を整えています。

  • 完全週休2日制(土日休み)で、祝日や夏季休暇、年末年始休暇もしっかり保証!
  • 様々な休暇制度(有給、慶弔、産前・産後、育児、バースデー休暇)を完備!
  • 従業員の成長と健康を支援するための表彰制度、資格取得支援、健康促進手当など!
  • 生活を支えるテレワーク手当、記事寄稿手当、結婚祝金・出産祝金など、様々な手当を提供!
  • 自己啓発としての書籍購入制度や、メンバー間のコミュニケーションを深める交流費補助!
  • 成果に応じた決算賞与や、リファラル採用手当、AI手当など、頑張りをしっかり評価!
  • ワークライフバランスを重視し、副業もOK!

株式会社 ONE WEDGEでは、一人ひとりの従業員が自己実現できる環境を大切にしています。
共に成長し、刺激を与え合える仲間をお待ちしております。
あなたの能力と熱意を、ぜひ当社で発揮してください。
ご応募お待ちしております!

ホームページ、採用情報は下記ボタンからご確認ください!

応募、ご質問など、LINEでお気軽にご相談ください♪

まとめ

ここまで、ListArrayListの違いと使い分け方について詳しく見てきました。
主な違いは、型の指定とパフォーマンスにあります。

List
  • ジェネリックなので型安全性が高い
  • 要素へのアクセスが高速
  • メモリ効率が良い
  • ボックス化・アンボックス化のオーバーヘッドがない
ArrayList
  • 様々な型の要素を格納できる柔軟性がある
  • 型の指定がないため、型の安全性は低い
  • ボックス化・アンボックス化のオーバーヘッドがある
使い分けのポイント
  • 要素の型が明確で、パフォーマンスが重要な場合はListを使う
  • 異なる型の要素を扱う必要がある場合や柔軟性が求められる場合はArrayListを選ぶ

また、他のデータ構造(HashSetDictionary、スタック、キュー)との比較も行いました。
データの特性や処理の要件に応じて、適切なデータ構造を選ぶことが重要です。

これらの知識を活かして、適材適所でリストを使い分けることで、より効率的で保守性の高いコードが書けるようになります。
実際のコードでListArrayListを使ってみて、その違いを体感してみてください。
パフォーマンスと可読性のバランスを考えながら、最適なデータ構造を選択していくスキルを身につけていきましょう。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です