Java での NoSuchElementException エラーを修正する

Mohammad Irfan 2023年10月12日
  1. Java でイテレータを使用しているときに NoSuchElementException が発生する
  2. Java で列挙型を使用しているときに NoSuchElementException が発生する
  3. Java で StringTokenizer を使用しているときに NoSuchElementException が発生する
  4. Java でスキャナークラスを使用しているときに NoSuchElementException が発生する
Java での NoSuchElementException エラーを修正する

例外は、プログラムの実行中に発生するイベントです。例外が発生すると、通常のプログラムフローが影響を受け、プログラムが異常終了します。このチュートリアルでは、java.util.NoSuchElementException とそれを Java で処理する方法について説明します。

NoSuchElementExceptionRuntimeException クラスを継承します。これは、チェックされていない例外であることを意味します。チェックされていない例外は、実行時に発生するため、コンパイラによって処理されません。

NoSuchElementException は、Scanner クラス、Iterator インターフェイス、Enumerator インターフェイス、および StringTokenizer クラスによってスローされます。これらのクラスには、イテラブルから次の要素をフェッチするためのアクセサのメソッドがあります。iterable が空であるか、最大制限に達した場合、NoSuchElementException をスローします。

さまざまなクラスが NoSuchElementException をスローする方法を見てみましょう。

Java でイテレータを使用しているときに NoSuchElementException が発生する

Iterator インターフェースには、反復の次の要素にアクセスするために使用される next() と呼ばれるメソッドがあります。コレクションに要素がない場合は、NoSuchElementException がスローされます。いくつかの例を見ていきます。

要素なしで HashMap を反復しようとしています。

import java.util.*;
public class MyClass {
  public static void main(String args[]) {
    // creating a hashmap with no element
    HashMap<String, Integer> h1 = new HashMap<>();
    // creating an iterator object
    Iterator i = h1.keySet().iterator();
    // trying to access element
    i.next();
  }
}

出力:

Exception in thread "main" java.util.NoSuchElementException
    at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1599)
    at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1620)
    at MyClass.main(MyClass.java:9)

HashMap が空であるため、next() メソッドは例外をスローします。hasNext() メソッドを使用して、この例外を回避できます。iterable にさらに要素がある場合は、true を返します。

このような例外を回避するために、hasNext() が True を返す場合にのみ、next() メソッドを使用する必要があります。以下の例を参照してください。

import java.util.*;
public class MyClass {
  public static void main(String args[]) {
    // creating a hashmap with no element
    HashMap<String, Integer> h1 = new HashMap<>();
    // creating an iterator object
    Iterator i = h1.keySet().iterator();
    // trying to access element
    while (i.hasNext()) {
      i.next();
    }
  }
}

このコードは例外をスローしません。HashMap にいくつかの要素がある例を見て、要素を繰り返しましょう。

import java.util.*;
public class MyClass {
  public static void main(String args[]) {
    // creating a hashmap
    HashMap<String, Integer> h1 = new HashMap<>();
    h1.put("one", 1);
    h1.put("two", 2);
    // creating an iterator object
    Iterator i = h1.keySet().iterator();
    // trying to access element
    while (i.hasNext()) {
      System.out.println(i.next());
    }
  }
}

出力:

one
two

hasNext() メソッドがなければ、このコードは例外をスローしていましたが、正常に機能しています。

Java で列挙型を使用しているときに NoSuchElementException が発生する

Java では、Enumeration には、列挙型の次の要素を返す nextElement() というメソッドがあります。返す要素がない場合は、NoSuchElementException がスローされます。

リストから列挙型を作成している以下の例を見てください。

import java.util.*;
public class MyClass {
  public static void main(String args[]) {
    ArrayList<String> animals = new ArrayList<>();
    animals.add(new String("elephant"));
    // creating enumeration object
    Enumeration en = Collections.enumeration(animals);
    System.out.println(en.nextElement()); // gets "elephant"
    System.out.println(en.nextElement()); // throws exception
  }
}

出力:

elephant

Exception in thread "main" java.util.NoSuchElementException
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:970)
    at java.base/java.util.Collections$3.nextElement(Collections.java:5440)
    at MyClass.main(MyClass.java:9)

hasElement() は、アクセスする ArrayList に要素が残っていないため、最初の要素を返した後に例外をスローします。この状況を回避するために、hasMoreElements() メソッドを使用できます。

列挙に提供する要素がさらにある場合、このメソッドは true を返します。それ以外の場合は、false を返します。列挙にさらに要素がある場合にのみ、nextElement() メソッドを呼び出すことができます。

以下の例を見てください。

import java.util.*;
public class MyClass {
  public static void main(String args[]) {
    ArrayList<String> animals = new ArrayList<>();
    animals.add(new String("elephant"));
    // creating enumeration object
    Enumeration en = Collections.enumeration(animals);
    while (en.hasMoreElements()) {
      System.out.println(en.nextElement()); // gets "elephant"
    }
  }
}

出力:

elephant

Java で StringTokenizer を使用しているときに NoSuchElementException が発生する

Java では、StringTokenizer クラスは、nextToken()nextElement() の 2つのメソッドを提供します。nextToken() メソッドは文字列トークナイザーから次のトークン(文字列型)を返しますが、nextElement メソッドは文字列ではなくオブジェクト型を返すことを除いて nexttoken() に似ています。どちらのメソッドも NoSuchElementException をスローします。

以下の例を参照してください。

import java.util.*;
public class MyClass {
  public static void main(String args[]) {
    String s = "I Love Delft";
    StringTokenizer st = new StringTokenizer(s);
    System.out.println(st.nextToken()); // gets I
    System.out.println(st.nextToken()); // gets Love
    System.out.println(st.nextToken()); // gets Delft
    System.out.println(st.nextToken()); // Throws exception
  }
}

出力:

I
Love
Delft

Exception in thread "main" java.util.NoSuchElementException
    at java.base/java.util.StringTokenizer.nextToken(StringTokenizer.java:347)
    at MyClass.main(MyClass.java:9)

hasMoreTokens() および hasMoreElements() メソッドを使用して例外を回避できます。トークナイザーの文字列でより多くのトークンが使用可能な場合、どちらのメソッドも true を返します。hasMoreTokens() メソッドが True を返す場合にのみ、nextToken() メソッドを呼び出す必要があります。

以下の例を参照してください。

import java.util.*;
public class MyClass {
  public static void main(String args[]) {
    String s = "I Love Delft";
    StringTokenizer st = new StringTokenizer(s);
    while (st.hasMoreTokens()) {
      System.out.println(st.nextToken());
    }
  }
}

出力:

I
Love
Delft

Java でスキャナークラスを使用しているときに NoSuchElementException が発生する

Java の Scanner クラスは、next()nextInt() などのいくつかのユーティリティメソッドを提供します。これらのメソッドを使用している間、NoSuchElementException をスローできます。ここでそれらについて説明します。

  1. 標準入力にアクセスする 2つのスキャナーオブジェクトがあるとします。それらの 1つを閉じて、もう 1つを使用してメソッドを呼び出すと、NoSuchElementException がスローされます。以下の例を参照してください。
import java.util.*;
public class MyClass {
  public static void main(String args[]) {
    String s = "I Love Delft";
    Scanner s1 = new Scanner(System.in);
    Scanner s2 = new Scanner(System.in);
    s1.close();
    s2.next();
  }
}

出力:

Exception in thread "main" java.util.NoSuchElementException
    at java.base/java.util.Scanner.throwFor(Scanner.java:937)
    at java.base/java.util.Scanner.next(Scanner.java:1478)
    at MyClass.main(MyClass.java:8)

最初のスキャナーを閉じると、基になる InputStream が閉じます。したがって、2 番目のスキャナーは同じ InputStream から読み取ることができず、NoSuchElementException をスローします。解決策は、1つのスキャナーオブジェクトを使用して System.in 入力を読み取ることです。

  1. スキャナーオブジェクトを使用して文字列またはファイルを読み取っているとします。読み取る行が残っていない場合は、例外が表示されます。以下の例を参照してください。
import java.util.*;
public class MyClass {
  public static void main(String args[]) {
    String s = "I Love Delft";
    Scanner s1 = new Scanner(s);
    System.out.println(s1.nextLine());
    System.out.println(s1.nextLine());
  }
}

出力:

I Love Delft

Exception in thread "main" java.util.NoSuchElementException: No line found
    at java.base/java.util.Scanner.nextLine(Scanner.java:1651)
    at MyClass.main(MyClass.java:7)

この問題を解決するために、ブール値を返す hasNextLine() メソッドを使用します。例を見てください。

import java.util.*;
public class Main {
  public static void main(String args[]) {
    String s = "I Love Delft";
    Scanner s1 = new Scanner(s);
    while (s1.hasNextLine()) {
      System.out.println(s1.nextLine());
    }
  }
}

出力:

I Love Delft

関連記事 - Java Error