1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C#/Unity】Dictionaryに値を追加すると発生する 「ArgumentException: An item with the same key has already been adde」の原因と解決法

Last updated at Posted at 2025-09-05

はじめに

Dictionaryを使ってデータを管理していると下記のエラーに遭遇しました。

ArgumentException: An item with the same key has already been added. Key: player1

普段あまりDictionaryでエラーが発生していなかったので戸惑いましたが、
エラー文面通り単純に同じキーを2回追加しようとしたときに発生するようです。

エラー再現コード

C#
void Start() {
    Dictionary<string, int> scores = new Dictionary<string, int>();

    scores.Add("player1", 100);
    scores.Add("player2", 200);

    // 同じキーを再度追加
    scores.Add("player1", 300); // ここでエラー
}

上記コードを実行するとUnityコンソールにArgumentExceptionが表示されます。
上記は単純な例ですが、よくあるのは下記コードのような
複数個所や同タイミングでDictionaryへ同一データを追加する処理を走らせてしまう場合などです。

C#
public static class SharedStore {
    public static Dictionary<string, int> scores = new Dictionary<string, int>();
}

public class ProcessorA {
    public void Run() {
        SharedStore.scores.Add("player1", 100);
        SharedStore.scores.Add("player1", 300);
    }
}

public class ProcessorB {
    public void Run() {
        // ProcessorAでも同じキーを追加している
        SharedStore.scores.Add("player1", 300);
    }
}

public class Main {
    static async Task Start() {
        var taskA = Task.Run(() => new ProcessorA().Run());
        var taskB = Task.Run(() => new ProcessorB().Run());
    }
}


自分はこんな感じの処理で発生していました。
(正直この場合は根本解決した方が良いと思いますが、状況によってはそうもいかない場合があると思いますのでエラーの解決方法を記載しますね)

原因・解決法

Dictionaryキーの重複を許しません。
Addメソッドでは、すでに存在するキーを追加しようとすると例外を投げます。

解決法1:TryAddを使用する

scores.TryAdd("player1", 300);

TryAddメソッドは、
キーが存在しない場合のみ追加し、成功すればtrue、失敗すればfalseを返します。
そのため、TryAddAddのように重複キーによる例外を発生させません
ただし、C#7.0以降の機能であり、.NET Core 2.0 / .NET Standard 2.1 / .NET Framework 非対応なので環境によっては使用に注意が必要です。

解決法2:ContainsKeyで事前チェック

if (!scores.ContainsKey("player1") {
    scores.Add("player1", 300);
}

解決法3:値の上書き

scores["player1"] = 300; // 存在すれば上書き、なければ追加

まとめ

今回のようなDictionaryで発生するArgumentExceptionの解決法のまとめになります。
Dictionaryはキーの重複を許さない
Addは重複時に例外を投げる
TryAdd / ContainsKey / インデクサでの上書き で回避可能


付録

この記事を見ている方はデータ管理等でDictionaryを有効活用して開発を行っているかと思います。
そこでUnity開発において、Dictionaryの使用に関する注意点、要点などを深堀してまとめておきます。
いずれかがお役にたてば幸いです。

重複キーが発生しやすい原因・対策

  • 多重初期化

同じGameObject/ScriptAwake/OnEnable/Startの複数箇所で同じデータを登録している。
Prefab + Instantiateやシーン跨ぎのシングルトン二重生成でも起こるため注意。
対策: 一意な初期化ガード(例: initialized フラグ)や DontDestroyOnLoad + シングルトン検出など。

  • イベントの多重購読

同じハンドラが複数回呼ばれて重複追加。
対策: 購読の対称性(OnEnableで登録→OnDisableで解除)と 重複購読防止。

  • キーの正規化不足:

大文字小文字・トリム不統一で別経路から同じ意味のキーが入る。
対策: 規約統一(例: ToLowerInvariant().Trim())と IEqualityComparer 指定。

  • 非同期/コルーチンの競合:

同一フレーム内に複数の Add が走ることがある。
対策: Upsert パターン(後述)や 排他制御(基本 Unity はメインスレッド前提)。

パフォーマンスに関して

  • 大量追加がわかっているなら 初期容量 を指定

var dict = new Dictionary<string, int>(capacity: 10_000);
  • アクセス最速パターン

既存値取得は TryGetValue が例外を避けて最速。

if (scores.TryGetValue(id, out var s)) { /* use s */ }
  • メモリと解放:

Clear() はサイズを保持する。大量解放後に縮めたいなら 新規インスタンスに置き換え。

データ追加時の設計観点

メソッド 重複時の挙動 目的 使い分け
Add 例外を投げる 厳密な一意性保証 基本
TryAdd 何もしない(false返す) 安全な初回追加 競合回避・初期化
dict[Key] = value 上書き or 追加 値の更新 状態更新

参考記事

・【C#】Dictionary.Addとインデクサーによる要素の追加で挙動がどう異なるか(重複するキーの場合にArgumentExceptionか上書き)
・【C#】Dictionaryの使い方と落とし穴

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?