Javaで学ぶ「アルゴリズムとデータ構造」(前半)
アルゴリズムとデータ構造は、Javaに限らずすべてのプログラミング言語で「土台」になる考え方です。 文法を覚えるだけでは、コードは書けても「うまい設計」や「速い処理」にはなかなか到達できません。 前半では、まずデータ構造とアルゴリズムの関係を整理し、 Javaでよく使う基本的なデータ構造(配列・ArrayList・LinkedList・スタック・キュー)を、 初心者向けにかみ砕いて解説していきます。 後半では、木構造・ハッシュ・グラフなど、より本格的な構造と探索アルゴリズムに踏み込みます。
データ構造とアルゴリズムの関係をイメージでつかむ
まず、「データ構造」と「アルゴリズム」の関係を言葉で整理しておきます。 データ構造は「データをどう持つか」、アルゴリズムは「そのデータをどう扱うか」です。 例えば、同じ「10件の数値」を扱うとしても、 配列で持つのか、リストで持つのか、キューで持つのかによって、 「取り出しやすさ」「追加しやすさ」「削除しやすさ」が変わります。
アルゴリズムは、そのデータ構造の上で動く「手順」です。 ソート、検索、探索、集計、フィルタリングなどはすべてアルゴリズムです。 つまり、データ構造を選ぶことは「アルゴリズムの土台を選ぶこと」であり、 アルゴリズムを選ぶことは「その土台の上でどう動くかを決めること」です。
この関係を意識できるようになると、 「とりあえず配列」「とりあえず ArrayList」ではなく、 目的に応じて構造と手順を選べるようになっていきます。
配列(int[])という一番素朴なデータ構造
Javaで最も基本的なデータ構造は「配列」です。 配列は「固定長の箱の並び」で、 インデックス(0, 1, 2, …)を指定して要素にアクセスします。
簡単な例を見てみます。
int[] scores = new int[5];
scores[0] = 80;
scores[1] = 90;
scores[2] = 75;
scores[3] = 88;
scores[4] = 92;
System.out.println(scores[1]); // 90
Java配列の特徴は次のようなものです。
長さが固定で、途中で増やしたり減らしたりできない インデックスを指定したアクセスが非常に速い メモリ上に連続して並ぶため、CPUにとって扱いやすい
アルゴリズムの世界では、 「配列に対してソートする」「配列に対して線形探索する」 といった形で、配列は最も基本的な土台になります。
ただし、要素数が変わるような場面では、 配列だけでは扱いづらくなります。 そこで登場するのが ArrayList や LinkedList です。
ArrayList:可変長の配列としてのリスト
ArrayList は、「中身は配列だが、長さを増やしたり減らしたりできる」リストです。 Javaのコレクションの中でも、最もよく使われるクラスの一つです。
簡単な例を見てみます。
import java.util.ArrayList;
ArrayList<Integer> scores = new ArrayList<>();
scores.add(80);
scores.add(90);
scores.add(75);
System.out.println(scores.get(1)); // 90
scores.add(88); // 要素数を増やせる
scores.remove(0); // 先頭を削除できる
JavaArrayList の本質は、「配列をラップして、使いやすくしたもの」です。 内部では配列を使っていて、要素数が増えるときには 新しい配列を確保してコピーすることで「可変長」を実現しています。
重要なポイントは、 インデックスアクセス(get(i))が速い一方で、 途中の要素を削除したり挿入したりすると、 後ろの要素をずらす必要があり、コストがかかるという点です。
アルゴリズムの観点では、 「ランダムアクセスが多い」「末尾に追加することが多い」 といった場面で ArrayList が向いています。
LinkedList:つながりで表現するリスト
LinkedList は、「要素同士がポインタでつながったリスト」です。 配列のように「連続した箱」ではなく、 「ノードが鎖のようにつながっている」イメージです。
簡単な例を見てみます。
import java.util.LinkedList;
LinkedList<Integer> scores = new LinkedList<>();
scores.add(80);
scores.add(90);
scores.add(75);
System.out.println(scores.get(1)); // 90
scores.addFirst(70); // 先頭に追加
scores.addLast(100); // 末尾に追加
scores.remove(2); // 真ん中を削除
JavaLinkedList の特徴は次のようなものです。
先頭や末尾の追加・削除が速い 途中の要素の追加・削除も、位置さえ分かれば速い インデックスアクセス(get(i))は、内部で前から順にたどるため遅い
内部的には、各要素が「前の要素」「次の要素」への参照を持つノードとして表現されています。 そのため、「つなぎ替え」が簡単で、 「途中に挿入」「途中を削除」といった操作が得意です。
アルゴリズムの観点では、 「順番にたどることが多い」「途中の挿入・削除が多い」 といった場面で LinkedList が向いています。
スタック(Stack):「後から入れたものが先に出る」構造
スタックは、「後から入れたものが先に出る(LIFO)」構造です。 本を積み重ねて、上から取り出すイメージが近いです。
Javaでは、古い Stack クラスもありますが、 最近は Deque(両端キュー)をスタックとして使うことが多いです。
簡単な例を見てみます。
import java.util.ArrayDeque;
import java.util.Deque;
Deque<String> stack = new ArrayDeque<>();
stack.push("A");
stack.push("B");
stack.push("C");
System.out.println(stack.pop()); // C
System.out.println(stack.pop()); // B
System.out.println(stack.pop()); // A
Javaスタックは、アルゴリズムの世界で非常に重要な役割を持ちます。
再帰の展開を自分で管理したいとき 式の評価(逆ポーランド記法など) 括弧の整合性チェック 深さ優先探索(DFS)の非再帰実装
など、多くの場面でスタックが登場します。
「最後に入れたものが最初に出る」という性質を理解しておくと、 アルゴリズムの設計が一気に楽になります。
キュー(Queue):「先に入れたものが先に出る」構造
キューは、「先に入れたものが先に出る(FIFO)」構造です。 行列に並んで、先に並んだ人から順に処理されるイメージです。
Javaでは、ArrayDeque や LinkedList をキューとして使うことが多いです。
簡単な例を見てみます。
import java.util.ArrayDeque;
import java.util.Queue;
Queue<String> queue = new ArrayDeque<>();
queue.offer("A");
queue.offer("B");
queue.offer("C");
System.out.println(queue.poll()); // A
System.out.println(queue.poll()); // B
System.out.println(queue.poll()); // C
Javaキューは、アルゴリズムの世界で次のような場面で使われます。
幅優先探索(BFS) タスクの順番待ち メッセージキュー イベント処理
「先に来たものから順に処理する」という性質は、 現実世界の多くの仕組みと対応しています。 そのため、キューを理解することは、 アルゴリズムだけでなくシステム設計の理解にもつながります。
基本データ構造とアルゴリズムのつながりを意識する
ここまでに登場した
配列 ArrayList LinkedList スタック キュー
は、どれも「Javaでアルゴリズムを書くときの土台」になります。
例えば、
配列や ArrayList に対してソートアルゴリズムを適用する キューを使って幅優先探索(BFS)を実装する スタックを使って深さ優先探索(DFS)を非再帰で書く LinkedList を使って途中挿入・削除の多い処理を効率化する
といった形で、データ構造とアルゴリズムは常にセットで登場します。
「どの構造の上で、どのアルゴリズムを動かすか」を意識できるようになると、 コードの質とパフォーマンスが大きく変わっていきます。
前半のまとめと後半への橋渡し
前半では、Javaのアルゴリズムとデータ構造を学ぶための土台として
データ構造とアルゴリズムの関係 配列(int[])の特徴と役割 ArrayList と LinkedList の違いと使いどころ スタック(LIFO)の性質とアルゴリズムでの役割 キュー(FIFO)の性質とアルゴリズムでの役割
を整理しました。
後半では、さらに一歩進んで
木構造(ツリー)と探索アルゴリズム ハッシュテーブル(HashMap)と高速検索 グラフ構造と DFS/BFS 計算量(O(n), O(n log n), O(1))の直感 セキュリティ・パフォーマンスの観点から見た「危険なデータ構造の使い方」
といったテーマを、具体的な Java コードとともに深掘りしていきます。
