java.lang.IllegalStateException は、「今の状態では、そのメソッドは呼べない」という例外です。 渡した値が悪いのではなく、呼ぶ順番やタイミングが合っていないときに出ます。原因はメッセージでほぼ決まるので、まずメッセージを見ます。

メッセージ 状態 直し方
stream has already been operated upon or closed 使い終わった Stream をもう一度使った Stream を作り直す
Duplicate key a (attempted merging values 1 and 2) toMap でキーが重なった 3つめの引数で、重なったときの扱いを決める
No match found Matcher で find() / matches() の前、または一致しないのに group() if (m.find()) の中で group()
Scanner closed 閉じた Scanner を読んだ 閉じるのは使い終わってから
Queue full 容量いっぱいのキューに add offer で false を受け取る
(メッセージなし) Iterator.remove() を next() の前、または2回続けて呼んだ next() 1回につき remove() 1回

確認環境:OpenJDK 21.0.10(Linux)で再現しました(確認日 2026年10月12日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「IllegalStateExceptionの直し方|Javaのメッセージ別一覧」

stream has already been operated upon or closed

Stream は1回しか使えません。 count() や collect() などの終端操作をしたあとに、同じ Stream をもう一度使うと出ます。

Stream<String> s = Stream.of("A", "B");
System.out.println(s.count());   // 2
System.out.println(s.count());   // ← 例外
2
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
    at java.base/java.util.stream.ReferencePipeline.count(ReferencePipeline.java:709)
    at S1.main(S1.java:2)

直し方は、使うたびに Stream を作り直すことです。 元のデータ(リスト)を変数に持ち、そこから毎回 stream() を呼びます。

List<String> list = List.of("A", "B");
long count = list.stream().count();
List<String> upper = list.stream().map(String::toLowerCase).toList();

// 作り方が長いときは Supplier にまとめる
Supplier<Stream<String>> src = () -> list.stream().filter(x -> !x.isBlank());
long n = src.get().count();
List<String> all = src.get().toList();

Stream をフィールドや変数に入れて使い回す書き方は、この例外の元になります。持つのはリスト、Stream はその場で作る、と決めておくと防げます。

Duplicate key(toMap のキーが重なった)

Collectors.toMap は、同じキーが2回出ると例外になります。 メッセージに、重なったキーと両方の値が出ます。

record U(String name, int age) {}
Map<String, Integer> m = Stream.of(new U("a", 1), new U("a", 2))
        .collect(Collectors.toMap(U::name, U::age));   // ← 例外
Exception in thread "main" java.lang.IllegalStateException: Duplicate key a (attempted merging values 1 and 2)
    at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:135)
    ...

直し方は、3つめの引数(重なったときの扱い)を渡すことです。

やりたいこと 3つめの引数 結果
あとの値を残す (x, y) -> y {a=2}
先の値を残す (x, y) -> x {a=1}
足し合わせる Integer::sum {a=3}
全部残す toMap ではなく groupingBy(U::name, mapping(U::age, toList())) {a=[1, 2]}
Map<String, Integer> m = Stream.of(new U("a", 1), new U("a", 2))
        .collect(Collectors.toMap(U::name, U::age, (x, y) -> y));   // {a=2}

ただし、重なるはずのないキーが重なったなら、データの誤りかもしれません。 黙って片方を捨てる前に、重なったキーを確かめます。詳しくは関連記事「Java ListをMapに変換する方法 toMapのキー重複エラー対処」で扱っています。

No match found(正規表現の Matcher)

Matcher.group() は、find() か matches() で一致したあとでしか呼べません。 呼ぶ前、または一致しなかったときに group() を呼ぶと出ます。

Matcher m = Pattern.compile("(\\d+)").matcher("abc123");
m.group(1);   // ← find() の前なので IllegalStateException: No match found
Matcher m = Pattern.compile("(\\d+)").matcher("abc123");
if (m.find()) {
    System.out.println(m.group(1));   // 123
} else {
    System.out.println("数字なし");
}

m.find(); の戻り値を見ずに group() を呼ぶと、一致しない文字列("abc")のときだけ例外になります。 テストで一致する文字列しか試していないと見落とすので、if で受けます。

Scanner closed

閉じた Scanner を読むと出ます。 よくあるのは、メソッドの中で Scanner を close() し、別のメソッドで同じ Scanner を使う形です。

Scanner sc = new Scanner(System.in);
read1(sc);   // 中で sc.close() している
read2(sc);   // ← IllegalStateException: Scanner closed

Scanner(System.in) を閉じると System.in も閉じます。 新しい Scanner(System.in) を作り直しても、2行目を読もうとしたところで NoSuchElementException: No line found になりました。そのため、System.in の Scanner は main で1つ作り、プログラムの最後まで閉じずに渡して使います。

Queue full

容量を決めたキュー(ArrayBlockingQueue など)がいっぱいのときに add すると出ます。

メソッド いっぱいのとき
add(e) IllegalStateException: Queue full
offer(e) false を返す
put(e) 空くまで待つ
offer(e, 時間, 単位) 決めた時間だけ待ち、空かなければ false
var q = new ArrayBlockingQueue<Integer>(1);
q.add(1);
boolean ok = q.offer(2);   // false(例外にならない)

いっぱいになるのがあり得る処理なら、offer の戻り値で分岐します。

Iterator.remove()(メッセージなし)

Iterator.remove() は、next() 1回につき1回だけ呼べます。 next() の前や、2回続けて呼ぶと、メッセージなしの IllegalStateException になりました。

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.isBlank()) {
        it.remove();          // next() のあとに1回
    }
}
// もっと短く書くなら
list.removeIf(String::isBlank);

似ているが別の例外

状況 例外
同じ Thread を2回 start() IllegalThreadStateException(IllegalStateException ではなく IllegalArgumentException の仲間)
値の無い Optional に orElseThrow() NoSuchElementException: No value present
shutdown() したスレッドプールに submit RejectedExecutionException
変更できないリストに add UnsupportedOperationException

自分で投げるとき:IllegalArgumentException との使い分け

例外 意味 例
IllegalArgumentException 渡された値がおかしい 年齢に -1 が渡された
IllegalStateException オブジェクトの状態が、その操作に合っていない 未接続なのに送信、確定済みの注文を変更
public void ship() {
    if (status != Status.PAID) {
        throw new IllegalStateException("未入金の注文は出荷できません: id=" + id + ", status=" + status);
    }
    status = Status.SHIPPED;
}

メッセージには「何をしようとして、今どの状態だったか」を入れると、ログだけで原因が分かります。IllegalArgumentException の使い分けは、関連記事「Java IllegalArgumentExceptionの使い分け早見表」で扱っています。

よくある質問

Q. メッセージがなく、原因が分かりません。 A. スタックトレースの1行目の at(例外を投げたメソッド)を見ます。Itr.remove なら Iterator、AbstractPipeline なら Stream、Matcher なら正規表現です。

Q. Stream をデバッグで peek や count して確かめたら、例外になりました。 A. 確かめるために終端操作をすると、その Stream は使い終わりになります。確かめるときも、元のリストから新しく Stream を作ります。

Q. toMap のキー重複は、例外にしておくべきですか。 A. 重ならない前提のデータ(主キーなど)なら、例外のままのほうが誤ったデータに気づけます。重なるのが当たり前のデータなら、3つめの引数で扱いを決めます。

Q. 例外を catch して無視してもいいですか。 A. 状態の食い違いを隠すことになるので、おすすめしません。呼ぶ順番を直すか、find() や offer() のように結果を返すメソッドに変えます。

まとめ

  • 「今の状態では呼べない」という例外。原因はメッセージで決まる
  • stream has already been operated upon or closed は Stream の使い回し。元のリストから毎回作る
  • Duplicate key は toMap のキー重複。3つめの引数で扱いを決める
  • No match found は find() の結果を見ずに group()。if (m.find()) で受ける
  • 自分で投げるなら、値の誤りは IllegalArgumentException、状態の誤りは IllegalStateException

参考・出典

確認日はいずれも 2026年10月12日です。

  • Java SE 21 API「IllegalStateException」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/IllegalStateException.html
  • Java SE 21 API「java.util.stream」(Stream operations and pipelines):https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/stream/package-summary.html
  • Java SE 21 API「Collectors.toMap」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/stream/Collectors.html
  • Java SE 21 API「Matcher」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Matcher.html
  • Java SE 21 API「BlockingQueue」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/BlockingQueue.html

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「IllegalStateExceptionの直し方|Javaのメッセージ別一覧」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?