LASSIC Media らしくメディア
スタックとキューの違い|特徴と使いどころ
「積んでおいたタスクを、なぜこの順番で消化するのか」「受け付けた依頼を、なぜ先着順で処理しないのか」。システムの仕様やライブラリの挙動を眺めていて、こうした疑問を抱いたことはないでしょうか。原因の多くは、内部で使われているデータ構造の性質にあります。データを一時的にため、取り出す順番を決める代表的な仕組みが、スタックとキューです。名前は聞いたことがあっても、両者の違いを人に説明できるかというと、案外あいまいなまま業務を進めている担当者は少なくありません。
本記事では、法人のIT事業部でシステム企画や開発ベンダーとのやり取りに携わる方に向けて、スタックとキューの違いを基礎から整理します。専門的な実装の細部ではなく、「どちらが先に出てくるのか」という順序のルールと、業務システムでの登場場面に焦点を当てます。
処理順序の設計は、要件定義書の中では地味な項目に見えるかもしれません。しかし、承認フローの差し戻し順序や、問い合わせ対応の優先順位など、業務の実態と食い違った順序で実装されると、運用が始まってから「思っていた動きと違う」というクレームにつながりやすい部分でもあります。発注側がスタックとキューの違いを言葉として理解しておくと、要件の伝え方そのものが変わってくるはずです。
つまずきやすいポイント
スタックとキューは、どちらも「データをためて、後から取り出す」という点では共通しています。違いは、取り出す順番だけです。ここが曖昧なまま話が進むと、設計レビューやベンダーとの打ち合わせで次のようなすれ違いが起きがちです。
- 「積み上げる」というイメージだけが先行し、キューも同じ順番で処理されると誤解する
- Undo機能と印刷待ちの列を、同じ仕組みで実現できると考えてしまう
- 「先に登録したものが先に出る」のか「後に登録したものが先に出る」のか、要件定義の段階で言葉が揃わない
- 「順番を守ってほしい」という要望だけを伝え、その順番が到着順なのか優先度順なのかを詰め切れていない
いずれのすれ違いも、根っこにあるのは同じ疑問です。「後から入れたものと、先に入れたもの、どちらを先に処理したいのか」。この一点さえ言葉にできれば、スタックとキューのどちらの発想を採用すべきかは自然に決まります。次の章から、それぞれの仕組みを見ていきましょう。
スタックの仕組み:後入れ先出し(LIFO)
スタックは、データを積み重ねて管理する構造です。新しく追加したデータは一番上に置かれ、取り出すときも一番上から順に取り出されます。この順番のルールを、後入れ先出し(LIFO:Last In, First Out)と呼ぶのが一般的です。
身近な例としては、食堂のトレー置き場や、洗濯物をかごに積み上げていく様子が挙げられます。一番上に置いたものから使う、という感覚は多くの方が日常的に経験している動きでしょう。積み重ねの途中や下にあるものを直接取り出すことはできず、常に上から順に触れていくのがスタックの基本的な動きになります。途中の一枚だけを引き抜こうとすると、上に乗っているものを一旦どかす必要が出てくる点も、日常のトレー置き場と同じ感覚です。
スタックの基本操作
| 操作名 | 内容 |
|---|---|
| push(プッシュ) | スタックの一番上にデータを追加する |
| pop(ポップ) | スタックの一番上のデータを取り出して除去する |
| peek(ピーク) | 一番上のデータを取り出さずに確認する |
データが積まれていない状態でpopを呼び出そうとする状態は「空(empty)」と呼ばれ、多くの実装ではエラーとして扱われます。逆に、積み上げられる上限を超えてpushしようとする状態は「オーバーフロー」と呼ばれ、こちらもあらかじめ想定しておくべき境界条件です。
業務の場面に置き換えると、承認申請の差し戻し履歴を思い浮かべると分かりやすいでしょう。申請者が内容を修正して再提出するたびに、差し戻しの記録が積み上がっていきます。承認者が「直前の差し戻し理由」を確認したいときは、一番新しく積まれた記録から遡って見ていくはずです。古い差し戻し理由を先に見せられても、現在の修正内容と結びつけにくく、かえって混乱を招きます。このように、直近の状態を優先して振り返りたい場面では、スタックの発想がそのまま業務要件に重なります。
キューの仕組み:先入れ先出し(FIFO)
キューは、データを列に並べて管理する構造です。後ろから追加されたデータは列の末尾に加わり、取り出すときは先頭から順に処理されます。この順番のルールを、先入れ先出し(FIFO:First In, First Out)と呼びます。
イメージしやすいのは、窓口に並ぶ行列でしょう。先に並んだ人から順に案内され、後から来た人は列の末尾に加わります。割り込みが発生しない限り、並んだ順番がそのまま処理される順番になる点が、スタックとの大きな違いです。
キューの基本操作
| 操作名 | 内容 |
|---|---|
| enqueue(エンキュー) | キューの末尾にデータを追加する |
| dequeue(デキュー) | キューの先頭のデータを取り出して除去する |
| peek(ピーク) | 先頭のデータを取り出さずに確認する |
キューにも、スタックと同様に空とオーバーフローという境界条件があります。列に誰も並んでいない状態でdequeueを呼べば処理は成立せず、列の長さに上限があるシステムでは、上限を超えたenqueueをどう扱うかを設計段階で決めておく必要があります。
業務の場面では、コールセンターの入電受付を思い浮かべると分かりやすいでしょう。電話をかけてきた順に案内担当へつながる仕組みは、まさにキューそのものです。回線が混み合って受付できる件数の上限に達したときにどう案内するか(後で折り返す旨のメッセージを流す、待ち時間の目安を伝えるなど)は、オーバーフロー時の扱いを業務要件として決めておく作業と言い換えられます。受け付けた順序を守ることが公平性につながる場面ほど、キューの考え方がなじみます。
スタックとキューの違いを整理する
ここまでの内容を、次の表に整理しました。両者の違いは取り出す順番に集約されますが、業務での使い分けを考える上では、代表的な用途まで押さえておくと理解が深まります。
| 観点 | スタック | キュー |
|---|---|---|
| 取り出す順番 | 後入れ先出し(LIFO) | 先入れ先出し(FIFO) |
| データを入れる操作 | push | enqueue |
| データを出す操作 | pop | dequeue |
| 身近なイメージ | 積み重ねたトレーや皿 | 窓口に並ぶ行列 |
| 空のときの扱い | popできずエラー等になる | dequeueできずエラー等になる |
| あふれたときの扱い | オーバーフローとして扱う | オーバーフローとして扱う |
| 典型的な用途 | Undo履歴、関数呼び出しの管理 | 印刷ジョブ、順番待ちの処理依頼 |
この表からも分かる通り、両者は「何を優先して先に処理したいか」という一点で枝分かれしています。直近に入れたものを優先するならスタック、先に入れたものを優先するならキューという整理で、業務要件との対応づけがしやすくなるはずです。
要件定義書やユーザーストーリーを書く段階で、「〜の順に処理する」という一文が出てきたら、それがスタック寄りの要件なのかキュー寄りの要件なのかを一度立ち止まって確認する習慣をつけておくと、後工程での認識違いを防ぎやすくなります。特に、複数の担当者が関わる要件定義では、「順番」という言葉の解釈が人によって割れることが少なくありません。表の左列にある観点を一つずつ確認しながら会話を進めると、言葉の行き違いを減らせるでしょう。
もう一つの見分け方として、「新しいものと古いもの、どちらを優先して画面に表示したいか」を考える方法もあります。通知一覧やお知らせ機能で、新着を上に表示したい場合はスタック的な並び、受け付けた順に古いものから消化していきたい場合はキュー的な並びと対応づけられます。画面設計の段階でこの観点を持っておくと、データの並び順を実装してから直す、という手戻りを防ぎやすくなるでしょう。
使いどころを具体的に見る
ここまでの整理を踏まえ、実際にスタックの発想が使われている場面、キューの発想が使われている場面を具体的に見ていきます。どちらも普段は意識せず使っている仕組みですが、名前と結びつけて理解しておくと、システムの挙動やベンダーからの提案を評価するときの判断材料になるでしょう。
スタックが向く場面
スタックは、直前の状態に戻る操作や、処理の呼び出し関係を管理する場面で使われます。
- ブラウザの「戻る」ボタン:訪れたページを積み上げ、直前のページから順に戻る
- アプリケーションのUndo機能:直近の操作を積み上げ、新しいものから取り消す
- プログラムの関数呼び出し:ある処理の中から別の処理を呼び出すたびに積み上げ、終わった処理から順に元の処理へ戻る
- 括弧の対応チェックや構文解析など、入れ子構造を扱う処理
いずれも「一番新しく積んだものから片付ける」という共通点があります。業務システムの企画段階では、操作履歴を積み上げて巻き戻す仕様を検討する際に、この考え方が土台になります。
社内システムの改修に携わる担当者の視点で言えば、リリース履歴の管理もスタックの発想と相性が良い領域です。あるバージョンを本番環境に反映した後、不具合が見つかって直前のバージョンに戻したい場合、参照すべきは「一番新しく反映したバージョン」であり、その前のバージョンではありません。ロールバックの仕様を検討する際に「直近のものから遡る」という順序を明確に言葉にしておくと、開発ベンダーとの認識合わせがしやすくなるでしょう。
プログラムの関数呼び出しも、スタックの代表的な使いどころです。ある処理が別の処理を呼び出し、その中でさらに別の処理を呼び出す、という入れ子の構造を組んだとき、内部では呼び出した順に処理が積み上げられ、終わった処理から順に呼び出し元へ戻っていきます。この仕組みがあるおかげで、プログラムは複雑に入り組んだ処理の呼び出し関係を、迷わずに元の場所へ戻していけるわけです。
キューが向く場面
キューは、受け付けた順番を守って処理していく場面で使われます。
- プリンターの印刷ジョブ:先に送信したジョブから順に印刷される
- コールセンターや窓口の順番待ち:受付順に案内する仕組み
- 業務システム間でのメッセージや処理依頼の受け渡し:受け付けた順に後続の処理へ渡す
- 大量アクセスが集中した際の順番待ち画面:アクセスした順にサーバーへの処理を割り当てる
業務システムの裏側では、注文や申込みのデータを受け付けた順に後続処理へ渡す、といった設計でキューの考え方が土台になっています。処理の公平性や順序の保証が求められる場面ほど、キューの出番は多くなるでしょう。
システム間連携の場面でも、キューの考え方はよく登場します。たとえば、複数の拠点から送られてくる受発注データを一箇所で受け付け、後続の在庫システムへ順次渡していくような構成です。送信元が同時に大量のデータを投げてきても、受け側のキューに一旦ためておき、処理できる速度に合わせて順番に取り出していけば、後続システムへの負荷を平準化できます。受け付けた順序を保ったまま処理量を調整できる点が、業務システムでキューが選ばれる理由の一つです。
ヘルプデスクのチケット管理システムも、キューの考え方が土台になっている例です。問い合わせを受け付けた順に一覧へ並べ、担当者が上から順に対応していく運用は、まさにFIFOの発想そのものといえます。もっとも、実際の運用では緊急度の高い問い合わせを先に回したいという要望も出てくるため、次章で触れる優先度キューの考え方を組み合わせて設計されるケースも見られます。
発展と注意点
スタックとキューには、それぞれの発展形にあたる構造も押さえておきたいところです。両端キュー(デック、deque)は、先頭と末尾のどちらからでもデータの追加と取り出しができる構造で、スタックとしてもキューとしても振る舞わせられる汎用的な仕組みです。片方の端だけを使えばスタックとして、両端をバランスよく使えばキューとして機能するため、実装の柔軟性を優先したいライブラリでよく採用されています。
優先度キューは、到着した順番ではなく、あらかじめ設定した優先度の高いデータから取り出す構造で、緊急度の異なる処理依頼をさばく場面などで使われます。たとえば、システム障害対応のチケット管理で「影響範囲が広い障害」を「軽微な問い合わせ」より先に対応したい場合、単純な到着順のキューでは要件を満たしにくいのが実情です。こうした場面では、優先度キューの発想を取り入れ、緊急度をあらかじめ数値やランクとして持たせておく設計が向いています。
どちらを選ぶべきかは、「どういう順序でデータを処理したいか」という一点で決まります。直近の状態を優先するのか、受け付けた順序を守るのか、あるいは優先度で並べ替えるのか。要件定義の段階で、次のような問いを自分たちに投げかけてみると、選び方が整理しやすくなります。
- 処理対象は、直近に発生したものを優先すべきか、それとも古いものから順に片付けるべきか
- 受け付けた順序をそのまま守る必要があるか、それとも緊急度で並べ替える余地があるか
- 先頭・末尾のどちらからも出し入れしたい場面があるか(あればデックの発想が候補になる)
これらの問いへの答えが明確になれば、開発ベンダーに要件を伝える際にも、単に「順番通りに処理してほしい」ではなく、「受け付けた順序を守るFIFOの考え方で」といった具体的な言葉で伝えられるようになるでしょう。
逆に、これらの問いへの答えを持たないまま実装を依頼してしまうと、ベンダー側が独自の判断で順序を決めてしまい、リリース後に「思っていた並びと違う」という指摘につながることもあります。小さな確認項目に見えますが、要件定義の初期段階で言葉にしておく価値は十分にあるでしょう。
なお、本記事は、データの出し入れの順番を定める「データ構造」としてのスタックとキューに焦点を当てています。プログラムの実行時に使われるメモリ領域としてのスタック(ヒープ領域との対比で語られるもの)や、システム間連携で使われるメッセージキュー製品といったミドルウェアの解説ではない点にご留意ください。
具体例で見るpushとpop、enqueueとdequeue
スタックの動きを、番号付きのデータで追ってみます。まず1をpushし、続けて2、3の順にpushすると、上から3、2、1と積み上がります。ここでpopを呼ぶと、一番上にある3が最初に取り出されるでしょう。次にpopすると2、その次に1という順番になります。後から入れたものほど早く出てくる、という点がよく分かる流れです。
キューの動きも同様に追ってみましょう。1をenqueueし、続けて2、3の順にenqueueすると、先頭から1、2、3という並びです。ここでdequeueを呼ぶと、先頭にある1が最初に取り出されます。次にdequeueすると2、その次に3という順番です。入れた順番のまま出てくる、という素直な流れになっています。
記号を使って表すなら、スタックは「push(1) → push(2) → push(3) → pop は 3 を返す」という流れになり、キューは「enqueue(1) → enqueue(2) → enqueue(3) → dequeue は 1 を返す」という流れになります(<や>といった比較演算子は登場しません)。この一往復を手を動かして追うだけで、両者の違いは実感として残るはずです。
操作の前後で中身がどう変わるかを、表としても整理しておきます。
| 操作 | スタックの中身(上端が右) | キューの中身(先頭が左) |
|---|---|---|
| 初期状態 | (空) | (空) |
| 1を追加 | [1] | [1] |
| 2を追加 | [1, 2] | [1, 2] |
| 3を追加 | [1, 2, 3] | [1, 2, 3] |
| 1回取り出す | 3を取得 → [1, 2] | 1を取得 → [2, 3] |
| もう1回取り出す | 2を取得 → [1] | 2を取得 → [3] |
データを追加した直後の中身はスタックもキューも同じ並びに見えますが、取り出す段になると挙動がはっきり分かれます。スタックは右端(最後に積んだ側)から、キューは左端(最初に並んだ側)から取り出される、という一点を押さえておけば、この表は自力で再現できるはずです。
実務で仕様を確認する際も、同じ考え方が使えます。あるシステムの一覧画面がどちらの順序で処理しているかを判断したいときは、まず1件目・2件目・3件目とデータを登録してみて、どちらが先に取り出される、あるいは表示されるかを実際に試してみるとよいでしょう。直近のものが先に出てくればスタック的な挙動、登録順に出てくればキュー的な挙動だと判断できます。仕様書だけでは読み取りにくい挙動も、この小さな確認作業で言語化しやすくなります。
まとめ
スタックとキューは、どちらも「データをためて後から取り出す」という共通の役割を持ちながら、取り出す順番のルールだけが正反対になっている構造です。この一点さえ押さえておけば、業務システムの中で両者がどのように使い分けられているかを見分けられるようになるでしょう。
- スタックは後入れ先出し(LIFO)で、push/popという操作で上端からデータを出し入れする
- キューは先入れ先出し(FIFO)で、enqueue/dequeueという操作で末尾から入れ先頭から出す
- Undoやブラウザの戻る操作、関数呼び出しの管理はスタックの考え方が土台になっている
- 印刷ジョブや順番待ち、業務システム間の処理依頼の受け渡しはキューの考え方が土台になっている
- 両端から出し入れできるデックや、優先度で並べ替える優先度キューといった発展形もある
- 選び方は「どの順序で処理したいか」という要件次第で決まる
- 本記事が扱うのは順序を定めるデータ構造としての概念であり、メモリ領域やメッセージキュー製品の話とは区別して理解しておく
データ構造の選定に迷ったら、LASSICにご相談ください
スタックとキューは基礎的な概念ですが、実際の業務システムでは「どの処理を優先するか」「同時にどれだけの依頼をため込めるようにするか」といった要件が複雑に絡み合います。処理順序の設計を誤ると、後工程での手戻りにつながりやすい領域です。
LASSICでは、要件定義の段階から処理順序の設計を含めて伴走し、業務フローに合った構造を一緒に検討します。国内外の開発体制を活かした柔軟な進め方もご提案できますので、設計の妥当性を第三者の視点で確認したい場合にもお役立ていただけるでしょう。すでに稼働しているシステムの挙動が想定と異なると感じている場合も、現状の処理順序を洗い出すところから支援いたします。
よくある質問
スタックとキューは、どちらか一方だけ理解しておけば十分ですか。
業務要件によって必要になる場面が異なるため、両方の考え方を押さえておくことをおすすめします。直近の状態を優先して戻したい要件ではスタックの発想が、受け付けた順序を守りたい要件ではキューの発想が土台になります。どちらか一方の知識だけでは、要件を正しく言葉にできない場面が出てくるでしょう。実際のシステムでは両方の構造が同じ仕組みの中で組み合わさっていることも多く、片方だけを見ていると設計の意図を読み違えることがあります。
デック(両端キュー)は、スタックやキューの代わりに使えますか。
デックは両端からデータを出し入れできる構造で、片方の端だけを使えばスタックとしても、キューとしても振る舞わせられます。実務で用途がはっきりしている場合は、意図を分かりやすく伝えるためにスタックやキューとして扱う方が、後から読む人にとって理解しやすいこともあります。設計書やコードにデックを使う場合でも、「実際にはスタックとして使っている」といった補足を残しておくと、後から見た担当者が意図を読み取りやすくなるでしょう。
優先度キューは、キューの一種と考えてよいですか。
優先度キューは、名前の通りキューを土台にした発展形ですが、取り出す順番は到着順ではなく優先度で決まります。緊急度の異なる処理依頼が混在する業務では、単純なキューよりも優先度キューの考え方が合う場合があります。要件によって使い分ける発想が大切です。「先着順で公平に扱いたいのか」「緊急度で並べ替えたいのか」を、要件定義の早い段階で関係者と合意しておくと、後から仕様を大きく変更する事態を避けやすくなるでしょう。
データ構造の設計は、開発ベンダーに任せておけば問題ありませんか。
実装そのものは開発ベンダーに任せられる領域ですが、「どの順序で処理してほしいか」という要件は発注側の業務理解に基づいて言葉にする必要があります。処理順序の要件があいまいなまま進むと、想定と異なる挙動になってから気づくケースも見受けられます。要件定義の段階で認識を合わせておくと、手戻りの少ない進め方につながるでしょう。発注側とベンダーの双方が「LIFO」「FIFO」といった共通の言葉を使えるようになるだけでも、仕様書のやり取りにかかる時間は短縮しやすくなります。
著者:テレリモ総研編集部 鈴木 亮佑
処理順序の設計から、ニアショア開発での実装まで
業務システムの処理順序をどう設計するかは、要件定義の段階で決めておきたいポイントです。国内のニアショア拠点を活用した開発体制なら、コミュニケーションの取りやすさを保ちながら、コストと品質のバランスを取った開発を進められます。
データ構造の選定を含めた設計相談から、実装・保守まで一貫してサポートいたします。要件定義から運用までを見据えた体制づくりにも対応できますので、処理順序の要件整理でお悩みの際は、LASSICにご相談ください。
出典
本記事の記述にあたり、以下の一次情報を参照しました。用語の定義や操作名の表記は、これらの公式資料に基づいています。
- Python公式ドキュメント「queue — 同期キュークラス」(FIFO・LIFOキューの定義)
- Python公式ドキュメント「collections — コンテナデータ型」(dequeとスタック・キューの関係)
- MDN Web Docs 用語集「コールスタック(Call stack)」