Java | Java 標準ライブラリ:LinkedHashMap

Java Java
スポンサーリンク

LinkedHashMap をざっくりイメージする

まず、すごくラフに言うと

「順番を覚えてくれる HashMap」

LinkedHashMap です。

HashMap は「キー → 値」を高速に扱える代わりに、順番は一切保証しませんでした。
LinkedHashMap

  • Map(キー → 値)の使い勝手はそのまま
  • さらに「どの順番で扱うか」をちゃんと覚えてくれる

というクラスです。

コードではだいたいこう書きます。

import java.util.LinkedHashMap;
import java.util.Map;

Map<String, Integer> map = new LinkedHashMap<>();
Java

左を Map、右を LinkedHashMap にするのは、他の Map と同じ“お約束”です。


HashMap との違いを直感でつかむ(ここは超重要)

HashMap は順番がぐちゃぐちゃ、LinkedHashMap は「順番を保つ」

まず、HashMap の挙動を見ておきます。

import java.util.HashMap;
import java.util.Map;

Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("apple", 1);
hashMap.put("banana", 2);
hashMap.put("orange", 3);

System.out.println(hashMap); // {orange=3, apple=1, banana=2} など、順番バラバラ
Java

入れた順番どおりに出てくるとは限りません。
ここに期待してはいけません。

同じことを LinkedHashMap でやるとどうなるか。

import java.util.LinkedHashMap;
import java.util.Map;

Map<String, Integer> linked = new LinkedHashMap<>();
linked.put("apple", 1);
linked.put("banana", 2);
linked.put("orange", 3);

System.out.println(linked); // {apple=1, banana=2, orange=3}
Java

入れた順番(挿入順)どおりに保たれます。

つまり、

  • HashMap → 「順番は未定義。バラバラ」
  • LinkedHashMap → 「挿入順を記憶してくれる」

という違いです。

「画面に表示するとき、登録した順番どおりに並べたい」のような場面では、
HashMap ではなく LinkedHashMap を選ぶべきです。


基本操作(put / get / remove)はほぼ HashMap と同じ

put / get / remove の挙動

基本的な使い方は HashMap とまったく同じです。

Map<String, String> users = new LinkedHashMap<>();

users.put("u001", "山田");
users.put("u002", "佐藤");
users.put("u003", "鈴木");

System.out.println(users.get("u002")); // 佐藤

users.remove("u001");

System.out.println(users); // {u002=佐藤, u003=鈴木}
Java

キーは一意で、同じキーに put すると上書きされる。
値は重複していてもよい。
get で取り出し、remove で削除、containsKey で存在チェック。

これらはすべて HashMap と同じです。

ループしたときに「順番が保たれる」という違い

優位性が出てくるのは、Map をループする場面です。

for (Map.Entry<String, String> e : users.entrySet()) {
    System.out.println(e.getKey() + " → " + e.getValue());
}
Java

LinkedHashMap なら、ここでの順番が「挿入した順番」になります。
HashMap だとバラバラです。

画面表示、ログ出力、ファイル保存など、「人間が見る順番」が意味を持つ場合、
ここがかなり効いてきます。


LinkedHashMap が「順番を覚えられる」仕組みのざっくりイメージ

内部的には、

  • HashMap のようにハッシュテーブルを持ちつつ
  • さらに「前後の要素へのリンク(双方向リスト)」も持っている

という構造になっています。

ざっくり言えば、HashMap に「順番用の LinkedList 機能」がくっついているイメージです。

この“鎖”のおかげで

  • どの要素が最初に追加されたか
  • 次はどれか
  • 最後はどれか

といった情報を簡単に辿れるようになっています。

その代わり、HashMap よりメモリを多く使い、
少しだけ重い(オーバーヘッドがある)とも言えます。

だから「順番が欲しいときだけ LinkedHashMap」を選ぶのが、設計として自然です。


「アクセス順」にも並べられるという裏機能(LRU もどきの基礎)

ここから少し踏み込んだ話です。
LinkedHashMap には、もう一つ重要なモードがあります。

コンストラクタで

new LinkedHashMap<>(初期容量, 負荷係数, accessOrder)
Java

という形を使うと、「順番の意味」を切り替えられます。

挿入順モード(デフォルト)

accessOrder を false(または指定なし)にすると、
今まで説明してきた「挿入順」が保たれます。

LinkedHashMap<String, Integer> map =
        new LinkedHashMap<>(16, 0.75f, false); // これが普通
Java

アクセス順モード(accessOrder = true)

accessOrder を true にすると、「アクセスされた順」に並び替えられるようになります。

LinkedHashMap<String, Integer> map =
        new LinkedHashMap<>(16, 0.75f, true);
Java

このモードでは、get(や put)でアクセスされるたびに、
そのエントリが「一番新しい位置(末尾)」に移動します。

簡単な例で見てみましょう。

LinkedHashMap<String, Integer> map =
        new LinkedHashMap<>(16, 0.75f, true);

map.put("A", 1);
map.put("B", 2);
map.put("C", 3);

System.out.println(map); // {A=1, B=2, C=3} (挿入順)

map.get("A");            // A にアクセス

System.out.println(map); // {B=2, C=3, A=1} (A が最後に移動)
Java

この機能を使うと、
「最近使ったもの」と「長いこと使っていないもの」を順番で表現できます。
いわゆる「LRU キャッシュ」の土台としてよく使われます。


LRU キャッシュ風の使い方をイメージしてみる(少し応用)

「最大 N 個まで覚えておき、溢れたら一番古いものから消す」というキャッシュを考えます。

LinkedHashMap は removeEldestEntry というメソッドをオーバーライドすることで、
「いつ一番古い要素を自動削除するか」を制御できます。

import java.util.LinkedHashMap;
import java.util.Map;

public class LruCache<K, V> extends LinkedHashMap<K, V> {
    private final int maxSize;

    public LruCache(int maxSize) {
        super(16, 0.75f, true); // accessOrder = true でアクセス順モード
        this.maxSize = maxSize;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > maxSize; // サイズが超えたら一番古いエントリを削除
    }
}
Java

使い方は普通の Map と同じです。

LruCache<String, String> cache = new LruCache<>(3);

cache.put("A", "a");
cache.put("B", "b");
cache.put("C", "c");

cache.get("A"); // A を最近使ったことになる

cache.put("D", "d"); // このタイミングで一番古い「B」が消されるイメージ
Java

初心者の段階でここまで実装できなくてもかまいません。
大事なのは

「LinkedHashMap は、挿入順だけでなく“アクセス順”も扱える。
 これを使うとキャッシュのようなものも作れる」

という感覚です。


LinkedHashMap を選ぶべき場面と、選ばなくていい場面

LinkedHashMap を選ぶべき場面

次のような条件が揃うとき、LinkedHashMap が向いています。

  • Map(キー → 値)で管理したい
  • でも「順番」も意味がある
    • 追加した順に表示したい
    • CSV や JSON として出力するときの順番を固定したい
    • 最近使った順に処理したい(アクセス順モード)

例えば、

  • 「最初に登録した商品から順に一覧表示したい商品マスタ」
  • 「設定項目を定義順にファイル保存したい設定 Map」
  • 「アクセス回数に応じて古いものを捨てる簡易キャッシュ」

こういうのは、HashMap より LinkedHashMap のほうが自然です。

HashMap で十分な場面

逆に、次のようなときは HashMap のままで構いません。

  • 順番は本当にどうでもいい(どの順に出てきても困らない)
  • ひたすら「キーで速く取れればいい」だけの用途
  • 表示時には、どうせ別途ソートする

順番に意味が無いのに LinkedHashMap を使うと、
「順番管理のオーバーヘッド」を払い損ねることになります。

順番が必要かどうか、ちゃんと自分に問いかけてから選ぶと、
設計が一段クリアになります。


まとめ:LinkedHashMap を頭の中でこう位置づける

初心者向けに、LinkedHashMap の本質をまとめるとこうです。

  • Map(キー → 値)としての基本は HashMap と同じ。
  • 追加した順番(挿入順)をそのまま覚えてくれる。
  • 特殊モード(accessOrder = true)では「アクセスされた順」に並べ替えてくれる。
  • そのために、ハッシュテーブル+「順番用のリンク構造」を内部に持っている。
  • 「順番まで含めて意味のある Map」が欲しいときに選ぶべきクラス。

タイトルとURLをコピーしました