はじめに|この記事で得られる価値
「レビューで『ここは ArrayList が良い』と言われたけれど、
なぜ 配列 ではダメなのか説明できない……。」
私たちが開発現場で何度も遭遇する、典型的なモヤモヤです。
サイズ固定・可変といった表面的な違いは知っていても、
なぜそれがパフォーマンス差につながるのかまでは曖昧になりがちです。
この記事では、その疑問をメモリ構造という視点から分解します。
「なんとなく」ではなく、技術的根拠を持って選択できる状態を目指しましょう。
結論:本質的な違いは「サイズ変更の可否」
まず結論です。
両者の最も重要な違いは、サイズを変更できるかどうかにあります。
- 配列 (Array):作成時にサイズが決まり、その後は変更できない固定長構造
- ArrayList:要素数に応じて内部サイズが変化する可変長構造
この差が、速度・メモリ効率・設計思想に直結します。
配列 (Array) とは何か
配列は、同一型データを連続したメモリ領域に格納する最小構成のデータ構造です。
構造が単純な分、参照処理が極めて高速という特徴があります。
int[] numbers = new int[3];
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
- メモリ配置が固定
- インデックスアクセスは一定時間(O(1))
- サイズ変更は不可
用語解説:配列(Array)
複数の同じ型のデータをまとめて管理できるデータ構造。Javaでは「int[]」などで宣言し、要素数は作成時に決まる。用語解説:メモリ領域
プログラムがデータを一時的に保存する場所。配列はこの領域に連続してデータを格納する。用語解説:O(1)
アルゴリズムの計算量を表す記号で「一定時間で処理できる」ことを意味する。
ArrayList とは何か
ArrayListは、配列を内部に持つコレクションクラスです。
サイズ管理をクラス側が肩代わりしてくれるため、私たちは要素数を意識せずに扱えます。
ArrayList<String> names = new ArrayList<>();
names.add("田中");
names.add("鈴木");
names.add("佐藤");
- 内部実装は配列
- 要素追加時に自動リサイズ
- 操作APIが豊富
(Javaのコレクションやリスト型の違いについては『Spring Boot実践ガイド|List型・Map型の違いとBeanクラスの基礎&応用』もご参照ください)
用語解説:ArrayList
Javaのコレクションフレームワークの一つ。内部的には配列を使い、要素の追加・削除が簡単にできる可変長リスト。用語解説:コレクション
複数のデータをまとめて扱うための仕組み。ArrayListやHashMapなどが含まれる。
5つの観点で比較:配列 vs ArrayList
| 観点 | 配列 | ArrayList |
|---|---|---|
| サイズ | 固定長 | 可変長 |
| 参照速度 | 非常に高速 | 高速(わずかなオーバーヘッドあり) |
| 追加・削除 | 不向き | 容易 |
| 扱える型 | プリミティブ / オブジェクト | オブジェクトのみ |
| 機能 | 最小限 | 豊富 |
重要なのは「どちらが優れているか」ではなく、
用途に対してどちらが適切かです。
なぜ性能差が出るのか:メモリ構造の違い
■ 配列が高速な理由
配列は、メモリ上に連続配置されます。
そのため、要素アクセスは次の計算だけで完了します。
先頭アドレス + (index × データサイズ)
分岐や探索が不要なため、CPUキャッシュ効率も高くなります。
用語解説:連続配置
データがメモリ上で途切れず並んでいる状態。CPUが効率よくアクセスできる。
■ ArrayListで発生するコスト
ArrayListは容量を超えると、以下の処理が走ります。
- より大きな配列を新規確保
- 既存データをすべてコピー
- 古い配列を破棄
この全件コピーが、追加処理のコスト源です。
ただし平均計算量は O(1) に近く、通常用途では問題になりません。
用語解説:全件コピー
配列のサイズを超えたとき、すべての要素を新しい配列に移し替える処理。これが追加時のコスト増加の原因。
実務向け判断フロー
- 要素数は最初から確定しているか? → Yes:配列 / No:ArrayList
- 頻繁な追加・削除があるか? → Yes:ArrayList
- プリミティブ型を大量に扱い、性能最優先か? → Yes:配列 / No:ArrayList
多くの業務アプリでは、ArrayListが第一選択になります。
実務でハマりやすい3つの罠
① Arrays.asList() は可変ではない
List<String> list = Arrays.asList("a", "b", "c");
list.add("d"); // UnsupportedOperationException
対策:
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
(Javaのエラーや例外の原因・対策については『Spring Boot NullPointerException完全対策|3大原因と実装例まとめ』もご参照ください)
用語解説:Arrays.asList()
配列をリストに変換するJavaのメソッド。ただし生成されるリストはサイズ変更不可。用語解説:UnsupportedOperationException
実行時に許可されていない操作を行った場合に発生する例外(エラー)。
② ArrayList<int> は使えない
ジェネリクスはオブジェクト型のみ対応です。
| primitive | wrapper |
|---|---|
| int | Integer |
| double | Double |
| boolean | Boolean |
用語解説:ジェネリクス
クラスやメソッドで扱うデータ型を、実行時ではなくコンパイル時に指定できるJavaの仕組み。ArrayList<String>など。用語解説:プリミティブ型
intやdoubleなど、Javaの基本的なデータ型。オブジェクトではない。用語解説:ラッパークラス
プリミティブ型をオブジェクトとして扱うためのクラス。例:int→Integer。
③ ループ中の remove() は危険
拡張for文中の削除は、ConcurrentModificationExceptionの原因になります。
numbers.removeIf(n -> n == 2);
用語解説:ConcurrentModificationException
コレクション(リストなど)をループ中に変更した場合に発生するエラー。安全に操作するにはIteratorを使う。用語解説:拡張for文
Javaのfor-each構文。コレクションや配列の全要素を簡単に繰り返し処理できる。
まとめ:選択基準を言語化できるようになる
- 配列:固定長・高速参照・低オーバーヘッド
- ArrayList:可変長・高い柔軟性・実務向き
- 性能差の正体はメモリ配置とコピーコスト
判断理由を説明できるようになると、
レビュー指摘は「修正」から「設計議論」に変わります。
ぜひ手元のコードで、配列とArrayListを意識的に使い分けてみてください。