Javaで複数のデータを扱うときに登場する代表的な選択肢が「配列(Array)」と「List」です。しかし、どちらを使うべきか迷った経験はないでしょうか?
特に初心者の方にとっては、両者の違いや使い分けの基準が曖昧に感じられることも多いはずです。
この記事では、Javaエンジニア歴10年以上の筆者が、配列とListの違い、使い分けの基準、実践例まで丁寧に解説します。パフォーマンスや柔軟性、メンテナンス性を踏まえ、適切な選択ができるようになります。
配列の特徴と使いどころ
基本的な特徴
配列は、Javaにおける最も基本的なデータ構造です。あらかじめ要素数を指定して宣言し、同じ型のデータを連続して格納できます。
int[] numbers = new int[5];
String[] fruits = {"apple", "banana", "orange"};
主な特徴は以下のとおりです。
- 要素数は固定:一度サイズを指定すると変更できません。
- インデックスによる高速アクセス:0番目から始まるインデックスで直接アクセスできるため、読み書きが高速です。
- プリミティブ型に対応:
int
、double
などの値をそのまま格納でき、ボクシングのオーバーヘッドがありません。 - メモリ効率が良い:データが連続したメモリ領域に格納されるため、メモリ使用量が最小限に抑えられます。
配列を使うべきケース
配列が適しているのは、以下のようなシンプルでパフォーマンス重視のケースです。
- 要素数が変化しない場面(例:曜日、月の名前など定数的なデータ)
- リアルタイム処理など高パフォーマンスが要求される場面(例:画像処理や音声処理)
- プリミティブ型の大量処理が必要な場合(ボクシングによるパフォーマンス劣化を回避)
- メモリ使用量を最小限に抑えたい場合
Listの特徴と使いどころ
基本的な特徴
List
は、Javaのコレクションフレームワークの中でも汎用性が高く、最もよく使われるインターフェースの1つです。中でもArrayList
は代表的な実装クラスです。
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
主な特徴は以下のとおりです。
- サイズが可変:要素の追加・削除が柔軟に行えます。
- 便利な操作メソッドが豊富:
add
、remove
、contains
、sort
など、日常的なデータ操作に便利な機能が揃っています。 - Stream APIとの親和性が高い:
list.stream()
を使えば、ラムダ式による柔軟なデータ処理が可能です。
Listを使うべきケース
Listが適しているのは、以下のような場面です。
- 要素数が動的に変化する可能性がある(例:ユーザー入力、データベースの検索結果)
- ソート、検索、フィルタなどの処理が頻繁に発生する(例:商品一覧やログのフィルタリング)
- 可読性や保守性を重視したビジネスロジックの実装(例:業務アプリケーションでのデータ管理)
配列とListの比較表
配列とList
(主にArrayList
)の違いを、用途別にわかりやすく比較しました。選定時の判断材料としてご活用ください。
比較項目 | 配列 | List(ArrayListなど) |
---|---|---|
サイズの可変性 | 固定(後から変更不可) | 可変(必要に応じて拡張・縮小可能) |
プリミティブ型対応 | 対応(int[] 、double[] など) | 非対応(Integer 、Double などラッパークラスが必要) |
アクセス速度 | 最高速(直接メモリアクセス) | 高速(内部配列への間接アクセス) |
操作の柔軟性 | 低い(基本的な操作のみ) | 高い(豊富なメソッド群) |
可読性・保守性 | シンプルだが拡張性は低い | 直感的で保守性・拡張性に優れる |
Stream API対応 | Arrays.stream() が必要 | list.stream() でそのまま利用可能 |
メモリ使用量 | 最小限 | やや多い(オブジェクトオーバーヘッド) |
判断に迷ったら?使い分けチェックリスト
以下のチェックポイントを参考にすれば、配列とListのどちらを使うべきかすぐに判断できます。
- ✅ 要素数があらかじめ決まっていて変化しない
→ 配列 を使うのが適しています。 - ✅
int
やdouble
などのプリミティブ型を大量に高速処理したい
→ 配列 がボクシングのコストを避けられるため有利です。 - ✅ 要素の追加・削除が頻繁に発生する
→ List(ArrayList
など)が柔軟で便利です。 - ✅ ソート・フィルター・検索といった操作を頻繁に行う
→ List のメソッドやStream APIとの親和性が役立ちます。 - ✅ コードの可読性や将来的な保守性を重視したい
→ List を使うと、後からの拡張や変更がしやすくなります。
実践例:配列とListの使い方比較
🔹 配列を使った例
String[] fruits = {"apple", "banana", "orange"};
for (String fruit : fruits) {
System.out.println(fruit);
}
ポイント:
- 要素数は固定。
for-each
でのループが簡潔。- 高速かつメモリ効率が良い。
🔸 Listを使った例
List<String> fruits = new ArrayList<>();
fruits.add("apple");
fruits.add("banana");
fruits.add("orange");
fruits.remove("banana"); // 要素の削除
fruits.sort(Comparator.naturalOrder()); // ソート
fruits.forEach(System.out::println); // 出力
ポイント:
- 要素の追加や削除が自在。
- ソートや検索もメソッド1つで実現可能。
- Stream APIとの連携もスムーズ。
よくある質問(FAQ)
List<int>
が使えないのはなぜ?-
Javaのジェネリクス(Generics)は、
int
やdouble
などのプリミティブ型を直接扱うことができません。そのため、List<Integer>
のようにラッパークラス(この場合はInteger
)を使う必要があります。これにより自動ボクシング・アンボクシングが発生し、パフォーマンスに影響する場合があります。 - 配列とListを相互に変換するには?
-
配列 → List に変換する方法:
String[] array = {"a", "b", "c"}; List<String> list = Arrays.asList(array);
List → 配列 に変換する方法:
List<String> list = Arrays.asList("a", "b", "c"); String[] array = list.toArray(new String[0]);
- ArrayListの内部実装はどうなっている?
-
ArrayListは内部で配列を使用しており、容量が不足すると新しい配列(通常1.5倍のサイズ)を作成し、既存要素をコピーします。この仕組みにより動的サイズを実現していますが、頻繁なリサイズはパフォーマンスに影響します。
// 初期容量を指定してリサイズを最小限に List<String> list = new ArrayList<>(expectedSize);
まとめ:配列とListの違いと選択基準
配列とListは、どちらもJavaで頻繁に使われるデータ構造ですが、それぞれに明確な特長があります。重要なのは、用途や要件に応じて正しく使い分けることです。
選択の指針
- 高パフォーマンス + 固定サイズ + プリミティブ型処理 → 配列
- 柔軟性 + 保守性 + 可読性 + Stream API活用 → List
実践的なアドバイス
- 迷ったらListから始める:後からの変更や拡張が容易
- パフォーマンスボトルネックが判明したら配列を検討:プロファイリング結果に基づいて判断
- 大量のプリミティブ型処理は配列を優先:メモリ効率とパフォーマンスの両面で有利
- ビジネスロジックではListを活用:コードの可読性と保守性が向上
適切な選択により、パフォーマンス、保守性、可読性のバランスが取れた高品質なJavaコードを書くことができます。要件に応じて柔軟に使い分け、必要に応じて相互変換も活用しながら、効率的な開発を進めましょう。