JAVA

最終更新日: 2022.11.11 (公開: 2022.08.02)

Javaの「compareToメソッド」で文字列や値を比較!使い方と比較方法

Javaの「compareToメソッド」で文字列や値を比較!使い方と比較方法

Javaの「compareToメソッド」は、文字列や値を比較するためのメソッドです。数値には「比較演算子」を使えば問題ありませんが、文字列やクラス、日付などに対しては比較演算子を使えません。compareToメソッドを使えば、複雑な要素の比較も行えます。

ただし、compareToメソッドは戻り値の扱い方が独特で、クラスによっても使い方や比較方法に違いがあります。本記事では、意外と奥が深いcompareToメソッドについて、使い方や比較方法・応用テクニックなどを、初心者向けのサンプルコード付きで解説します。

Javaの「compareToメソッド」とは

Javaの「compareToメソッド」とは

Javaの「compareToメソッド」とは、文字列や値などの大小関係を比較するためのメソッドです。左辺と右辺の2つの値を比較し、どちらが大きいか・小さいか、もしくは等しいかをチェックできます。intやdoubleのような「プリミティブ型(基本データ型)」であれば、下記のように比較演算子を使えば比較できます。

//サンプルプログラム
import java.util.Scanner;

public class Main {

  public static void main(String[] args) {

    // 2つの変数に値を格納する

    int a = input(1);

    int b = input(2);

    // 2つの値の大小比較を行う

    if (a < b) {

      System.out.println(a + “は” + b + “より小さい”);

    } else if (a > b) {

      System.out.println(a + “は” + b + “より大きい”);

    } else {

      System.out.println(a + “と” + b + “は等しい”);

    }

  }

  // キーボード入力を受け付けて、入力された値を返すメソッド

  // さまざまな結果を表示するためのメソッドなので、こちらの処理内容は無視してOKです

  public static int input(int count) {

    // キーボード入力を受け付けるScannerを生成する

    Scanner scanner = new Scanner(System.in);

    // 整数値を受け付け、不正値の場合は再入力を求める

    System.out.print(count + “つ目の数値を入力してください:”);

    while (!scanner.hasNextInt()) {

      System.out.print(count + “つ目の数値を入力してください:”);

      scanner.next();

    }

    // 正常値を返す

    return scanner.nextInt();

  }

}

 

//実行結果
1つ目の数値を入力してください:1

2つ目の数値を入力してください:2

1は2より小さい

下の方に「input」というメソッドがありますが、これはキーボードからさまざまな値を入力し、その結果を確認するためのものです。本題は数値比較のほうなので、inputメソッド内部については意識しなくてもOKです。

mainメソッドでは、比較演算子を使った大小比較を行っています。しかし、文字列や日付、クラスなどの場合は比較演算子が使えません。これらの値の比較に、今回ご紹介する「compareToメソッド」が役立ちます。compareToメソッドは、単に等しいかどうか判断するだけではなく、「左右のどちらが大きいか・小さいか」がわかることもポイントです。

compareToメソッドの構文と戻り値の比較方法

compareToメソッドの構文と戻り値の比較方法

compareToメソッドは、クラスに搭載されているメソッドです。そのため、基準となる値のインスタンスからcompareToメソッドを呼び出し、比較対象のインスタンスを引数として渡します。compareToメソッドの構文は下記のとおりです。

変数A.compareTo(変数B);

呼び出し方自体は、通常のメソッドと変わりません。「a < b」や「a > b」が、「a.compareTo(b)」の形に変わるだけだと考えるとわかりやすいでしょう。ただし、大小関係の比較は戻り値で行う点が、比較演算子との大きな違いです。compareToメソッドの戻り値は基本的に3種類で、それぞれ下記のような意味があります。

戻り値の種類 戻り値の意味 具体例
-1 「変数A」が「変数B」より小さい 変数Aが1、変数Bが2のとき
0 「変数A」と「変数B」が等しい 変数Aが1、変数Bが1のとき
1 「変数A」が「変数B」より大きい 変数Aが2、変数Bが1のとき

少しわかりづらいかもしれませんが、すべては「左側」の値を基準とした「変数A – 変数B」の符号になると考えましょう。左側の変数Aを基準とするので、右側の変数Bより小さい場合、「変数A – 変数B」はマイナスになりますよね。一方で、変数Aのほうが大きければプラスになります。下記のサンプルプログラムで、一連の流れを確認しておきましょう。

//サンプルプログラム
import java.util.Scanner;

public class Main {

  public static void main(String[] args) {

    // 2つの変数に値を格納する

    Integer a = input(1);

    Integer b = input(2);

    // 2つの値の大小比較を行う

    int result = a.compareTo(b);

    System.out.println(a + “と” + b + “の比較結果:” + result);

    // 比較結果を基に詳細な結果を表示する

    if (result < 0) {

      System.out.println(a + “は” + b + “より小さい”);

    } else if (result > 0) {

      System.out.println(a + “は” + b + “より大きい”);

    } else {

      System.out.println(a + “と” + b + “は等しい”);

    }

  }

  // キーボード入力を受け付けて、入力された値を返すメソッド

  // さまざまな結果を表示するためのメソッドなので、こちらの処理内容は無視してOKです

  public static Integer input(int count) {

    // キーボード入力を受け付けるScannerを生成する

    Scanner scanner = new Scanner(System.in);

    // 整数値を受け付け、不正値の場合は再入力を求める

    System.out.print(count + “つ目の数値を入力してください:”);

    while (!scanner.hasNextInt()) {

      System.out.print(count + “つ目の数値を入力してください:”);

      scanner.next();

    }

    // 正常値を返す

    return scanner.nextInt();

  }

}

 

//実行結果
1つ目の数値を入力してください:100

2つ目の数値を入力してください:-100

100と-100の比較結果:1

100は-100より大きい

先ほどのサンプルプログラムと同じように、キーボードから値の入力を受け付け、その内容に応じた結果が表示されるようになっています。上記の例では「100」と「-100」を入力し、比較結果は「1」となりました。ちなみに、今回は整数型のラッパークラス「Integer」を使っていますが、これは後述するようにプリミティブ型ではcompareToメソッドが使えないからです。

さまざまなクラスごとのcompareToメソッドの使い方

さまざまなクラスごとのcompareToメソッドの使い方

前述したように、compareToメソッドはクラスに搭載されているメソッドですが、クラスによってその使い方や意味合いが異なります。基本知識を踏まえて、本章では下記3種類のクラスごとに、compareToメソッドの使い方を見ていきましょう。

  • Numberのサブクラスでは「値の大小関係」を比較する
  • Stringクラスでは「辞書的な位置関係」を比較する
  • Dateクラスでは「日付の前後関係」を比較する
  • Calendarクラスでは「日付の前後関係」を比較する

Numberのサブクラスでは「値の大小関係」を比較する

「Numberクラス」は、intやdoubleなどのプリミティブ型の「ラッパークラス」で、さまざまなクラスやメソッドと数値をやり取りするために使います。Numberクラスから派生した、Byte・Short・Integer・Long・Float・Double・BigInteger・BigDecimalでは、compereToメソッドが使えます。いずれも下記のように、基本的な使い方は同じです。

//サンプルプログラム
import java.math.BigDecimal;

import java.util.Scanner;

public class Main {

  public static void main(String[] args) {

    // 2つの変数に値を格納する

    BigDecimal a = input(1);

    BigDecimal b = input(2);

    // 2つの値の大小比較を行う

    int result = a.compareTo(b);

    System.out.println(a + “と” + b + “の比較結果:” + result);

    // 比較結果を基に詳細な結果を表示する

    if (result < 0) {

      System.out.println(a + “は” + b + “より小さい”);

    } else if (result > 0) {

      System.out.println(a + “は” + b + “より大きい”);

    } else {

      System.out.println(a + “と” + b + “は等しい”);

    }

  }

  // キーボード入力を受け付けて、入力された値を返すメソッド

  // さまざまな結果を表示するためのメソッドなので、こちらの処理内容は無視してOKです

  public static BigDecimal input(int count) {

    // キーボード入力を受け付けるScannerを生成する

    Scanner scanner = new Scanner(System.in);

    // 整数値を受け付け、不正値の場合は再入力を求める

    System.out.print(count + “つ目の数値を入力してください:”);

    while (!scanner.hasNextInt()) {

      System.out.print(count + “つ目の数値を入力してください:”);

      scanner.next();

    }

    // 正常値を返す

    return new BigDecimal(scanner.nextInt());

  }

}

 

//実行結果
1つ目の数値を入力してください:-10

2つ目の数値を入力してください:10

-10と10の比較結果:-1

-10は10より小さい

 

Stringクラスでは「辞書的な位置関係」を比較する

StringクラスのcompareToメソッドは、他のクラスとは性質が異なり、戻り値は「1」「0」「-1」に限らず「辞書的な位置関係」を示します。そのため、明らかな大小関係はありませんが、戻り値がマイナスなら左側が右側より小さい、プラスなら左側のほうが大きいという点では同じです。

また、「辞書式」の評価方法は、辞書においてその文字列が先に現れる場合は「小さい」、あとに現れるほうが「大きい」と判断します。JavaのcompareToメソッドは、「Unicode」という最もメジャーな文字コードで、辞書式評価を行っていることが特徴です。

Unicodeといっても基本的には紙の辞書と同じで、アルファベットはa→b→cと並び、数字は1→2→3と並んでいます。2つの文字列が一致した場合は「0」を返します。一方で、文字列Aが文字列Bより辞書式で小さい場合は負の値、大きい場合は正の値を返します。

ただし、compareToメソッドで文字列を比較する場合、2つの文字列を同じ種類に限定しないとうまくいきません。たとえば、「ひらがな」と「カタカナ」が混在したり、「カタカナ」と「英文字」が混在していると、文字コードの関係上うまく比較できないのです。下記のサンプルコードで、上記のポイントを検証していきましょう。

//サンプルプログラム
import java.util.Scanner;

public class Main {

  public static void main(String[] args) {

    // 2つの変数に値を格納する

    String a = input(1);

    String b = input(2);

    // 2つの値の大小比較を行う

    int result = a.compareTo(b);

    System.out.println(a + “と” + b + “の比較結果:” + result);

    // 比較結果を基に詳細な結果を表示する

    if (result < 0) {

      System.out.println(a + “は” + b + “より小さい”);

    } else if (result > 0) {

      System.out.println(a + “は” + b + “より大きい”);

    } else {

      System.out.println(a + “と” + b + “は等しい”);

    }

  }

  // キーボード入力を受け付けて、入力された値を返すメソッド

  // さまざまな結果を表示するためのメソッドなので、こちらの処理内容は無視してOKです

  public static String input(int count) {

    // キーボード入力を受け付けるScannerを生成する

    Scanner scanner = new Scanner(System.in);

    // 文字列の入力を受け付ける

    System.out.print(count + “つ目の文字列を入力してください:”);

    // 正常値を返す

    return scanner.next();

  }

}

 

//実行結果1
1つ目の文字列を入力してください:abc

2つ目の文字列を入力してください:abc

abcとabcの比較結果:0

abcとabcは等しい

 

//実行結果2
1つ目の文字列を入力してください:123

2つ目の文字列を入力してください:456

123と456の比較結果:-3

123は456より小さい

 

//実行結果3
1つ目の文字列を入力してください:aaa

2つ目の文字列を入力してください:AAA

aaaとAAAの比較結果:32

aaaはAAAより大きい

 

//実行結果4
1つ目の文字列を入力してください:Code

2つ目の文字列を入力してください:カキタイ

Codeとカキタイの比較結果:-12392

Codeはカキタイより小さい

今回は複数の実行結果をご紹介します。なお、上記のサンプルプログラムもキーボードから自由に入力できるので、さまざまな文字列の比較をお試しください。注目したいのは3番目と4番目の実行結果です。

「aaa」と「AAA」の比較結果が「32」になっていますが、これはUnicodeで「a」と「A」の文字コードが32離れているからです。ただし、compareToメソッドは大文字と小文字を区別するので、正確な比較はできません。

「Code」と「カキタイ」の比較では、非常に大きなマイナス値になっていますが、これは文字の種類が異なって正確な比較ができないからです。なお、大文字と小文字を区別したくない場合は、下記のサンプルコードのように「compareToIgnoreCase」メソッドを使いましょう。

//サンプルプログラム
import java.util.Scanner;

public class Main {

  public static void main(String[] args) {

    // 2つの変数に値を格納する

    String a = input(1);

    String b = input(2);

    // 2つの値の大小比較を行う

    int result = a.compareToIgnoreCase(b);

    System.out.println(a + “と” + b + “の比較結果:” + result);

    // 比較結果を基に詳細な結果を表示する

    if (result < 0) {

      System.out.println(a + “は” + b + “より小さい”);

    } else if (result > 0) {

      System.out.println(a + “は” + b + “より大きい”);

    } else {

      System.out.println(a + “と” + b + “は等しい”);

    }

  }

  // キーボード入力を受け付けて、入力された値を返すメソッド

  // さまざまな結果を表示するためのメソッドなので、こちらの処理内容は無視してOKです

  public static String input(int count) {

    // キーボード入力を受け付けるScannerを生成する

    Scanner scanner = new Scanner(System.in);

    // 文字列の入力を受け付ける

    System.out.print(count + “つ目の文字列を入力してください:”);

    // 正常値を返す

    return scanner.next();

  }

}

 

//実行結果1
1つ目の文字列を入力してください:aaa

2つ目の文字列を入力してください:AAA

aaaとAAAの比較結果:0

aaaとAAAは等しい

 

//実行結果2
1つ目の文字列を入力してください:aaa

2つ目の文字列を入力してください:AAb

aaaとAAbの比較結果:-1

aaaはAAbより小さい

上記のように、compareToIgnoreCaseメソッドを使った場合は、大文字と小文字が混在していても正確な判定ができます。実際に1番目の結果では、「aaa」と「AAA」は同じ文字列だと判定されました。2番目の実行結果では、「aaa」と「AAb」で比較した場合、aaaのほうが1だけ小さいと判定されるなど、正しい結果が出るようになります。

Dateクラスでは「日付の前後関係」を比較する

「Date」クラスは、日付や時間などを管理できる便利なクラスです。実は、プログラムで日付を管理するのは意外と難しいものです。文字列で管理すると、日付の計算や変更が容易ではありません。さらに、自作クラスを作るのはもっと難しいものです。

このDateクラスでcompareToメソッドを使うと、どれほど大きな差がある日付を比較しても、結果はNumberクラスなどと同じく「1」「0」「-1」いずれかになります。なお、Dateクラスは普段あまり使うことはありませんが、業務用プログラムなどではよく使われるのでぜひチェックしておきましょう。

//サンプルプログラム
import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.Scanner;

public class Main {

  // 日付表示用のフォーマットを生成する

  private static String format = new String(“yyyy/MM/dd”);

  private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);

  public static void main(String[] args) {

    // 2つの変数に値を格納する

    Date a = input(1);

    Date b = input(2);

    // 2つの値の大小比較を行う

    int result = a.compareTo(b);

    // それぞれの表示フォーマットを文字列化する

    String formatA = simpleDateFormat.format(a);

    String formatB = simpleDateFormat.format(b);

    System.out.println(formatA + “と” + formatB + “の比較結果:” + result);

    // 比較結果を基に詳細な結果を表示する

    if (result < 0) {

      System.out.println(formatA + “は” + formatB + “より小さい”);

    } else if (result > 0) {

      System.out.println(formatA + “は” + formatB + “より大きい”);

    } else {

      System.out.println(formatA + “と” + formatB + “は等しい”);

    }

  }

  // キーボード入力を受け付けて、入力された値を返すメソッド

  // さまざまな結果を表示するためのメソッドなので、こちらの処理内容は無視してOKです

  public static Date input(int count) {

    // キーボード入力を受け付けるScannerを生成する

    Scanner scanner = new Scanner(System.in);

    // Dateクラスを配列形式で生成する(parseメソッドにダブルポインタを渡すため)

    Date[] date = new Date[1];

    // 日付の入力を受け付け、不正値の場合は再入力を求める

    do {

      System.out.print(count + “つ目の日付を「” + format + “」形式で入力してください:”);

    } while (!parse(scanner.next(), date));

    return date[0];

  }

  public static boolean parse(String string, Date[] date) {

    // simpleDateFormat.parseメソッドは、例外のcatchが強制される「検査例外」

    try {

      // 入力された文字列を日付形式に変換する

      date[0] = simpleDateFormat.parse(string);

    } catch (ParseException e) {

      // 不正値の場合はfalseを返す

      return false;

    }

    // 正常値の場合はtrueを返す

    return true;

  }

}

 

//実行結果
1つ目の日付を「yyyy/MM/dd」形式で入力してください:2022/01/01

2つ目の日付を「yyyy/MM/dd」形式で入力してください:2022/12/31

2022/01/01と2022/12/31の比較結果:-1

2022/01/01は2022/12/31より小さい

複雑なサンプルプログラムになりましたが、自身で好きな日付を入力して実行結果を確認できるので、いろいろ試してみてください。重要なポイントは「int result = a.compareTo(b);」の部分と、mainメソッドの最後の比較部分です。

この部分だけを見ると、やはりIntegerやBigDecimalなどのラッパークラスと同じだということがわかります。実際に、入力した日付に応じた、適切な結果が表示されるはずです。

なお、途中で「Date[] date = new Date[1];」と、Dateオブジェクトをあえて配列で作成している部分があります。これは、parseメソッドに「参照の参照」を渡すためです。parseメソッドに渡したdate配列自体は「参照の値渡し」でしかありませんが、間接的には中身の「date[0]」は参照の参照になっています。

「date[0] = simpleDateFormat.parse(string);」では、キーボードで入力した文字列から、Dateオブジェクトを生成しています。このとき、dateが配列になっていなければ、メソッドの呼び出し元にあるdateの中身が変更されないのです。Javaではこのように、メソッドの引数でテクニックが必要なことがあるので、余裕がある人は参考にしてみてください。

Calendarクラスでは「日付の前後関係」を比較する

「Calendar」クラスは、先ほどのDateクラスとよく似ています。しかし、Dateクラスは指定した日付や時間を取得するために使用し、Calendarクラスは日付や時刻の演算を行うために使う点で異なります。

Dateクラスと同じく、Calendarクラスはどれほど大きな差がある日付を比較しても、結果は「1」「0」「-1」いずれかになります。Calendarクラスのサンプルコードを見ていきましょう。

//サンプルプログラム
import java.text.DateFormat;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Calendar;

import java.util.Scanner;

public class Main {

  // 日付表示用のフォーマットを生成する

  private static String format = new String(“yyyy/MM/dd”);

  private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);

  public static void main(String[] args) {

    // 2つの変数に値を格納する

    Calendar a = input(1);

    Calendar b = input(2);

    // 2つの値の大小比較を行う

    int result = a.compareTo(b);

    // それぞれの表示フォーマットを文字列化する

    String formatA = simpleDateFormat.format(a.getTime());

    String formatB = simpleDateFormat.format(b.getTime());

    System.out.println(formatA + “と” + formatB + “の比較結果:” + result);

    // 比較結果を基に詳細な結果を表示する

    if (result < 0) {

      System.out.println(formatA + “は” + formatB + “より小さい”);

    } else if (result > 0) {

      System.out.println(formatA + “は” + formatB + “より大きい”);

    } else {

      System.out.println(formatA + “と” + formatB + “は等しい”);

    }

  }

  // キーボード入力を受け付けて、入力された値を返すメソッド

  // さまざまな結果を表示するためのメソッドなので、こちらの処理内容は無視してOKです

  public static Calendar input(int count) {

    // キーボード入力を受け付けるScannerを生成する

    Scanner scanner = new Scanner(System.in);

    // Calendarクラスを配列形式で生成する(parseメソッドにダブルポインタを渡すため)

    Calendar[] calendar = new Calendar[1];

    calendar[0] = Calendar.getInstance();

    // 日付の入力を受け付け、不正値の場合は再入力を求める

    do {

      System.out.print(count + “つ目の日付を「” + format + “」形式で入力してください:”);

    } while (!parse(scanner.next(), calendar));

    return calendar[0];

  }

  public static boolean parse(String string, Calendar[] calendar) {

    // DateFormat.parseメソッドは、例外のcatchが強制される「検査例外」

    try {

      // 入力された文字列を日付形式に変換する

      calendar[0].setTime(DateFormat.getDateInstance().parse(string));

    } catch (ParseException e) {

      // 不正値の場合はfalseを返す

      return false;

    }

    // 正常値の場合はtrueを返す

    return true;

  }

}

 

//実行結果
1つ目の日付を「yyyy/MM/dd」形式で入力してください:2022/01/01

2つ目の日付を「yyyy/MM/dd」形式で入力してください:2022/12/31

2022/01/01と2022/12/31の比較結果:-1

2022/01/01は2022/12/31より小さい

基本的には、Dateクラスとほとんど同じです。ただし、Calendarクラスはインスタンスの生成方法が独特で、「calendar[0] = Calendar.getInstance();」のように「getInstance」メソッドを呼び出す必要があります。これは、Calendarが「抽象クラス(abstractクラス)」だからです。抽象クラスはインスタンスを生成できないのです。

しかし実際には、Calendarクラスには「getInstanceメソッド」があり、インスタンスを作ること自体は可能になります。getInstanceメソッド内部では、PCの環境設定などに応じて、適切なサブクラスのオブジェクトを生成して返しています。

このように、パラメーターに応じて生成するオブジェクトを変えるデザインパターンを、「ファクトリーメソッド(Factory Method)」と呼ぶのです。ファクトリーメソッドの魅力は、「再利用しやすいクラス」を作ることができ、よりオブジェクト指向的なプログラミングができることです。

実際に、下記のサンプルプログラムでファクトリーメソッドの雰囲気がわかるので、気になる人はぜひ参考にしてみてください。

//サンプルプログラム
// カレンダーのオリジナルクラス

abstract class Calendar {

  // カレンダー名

  String name = new String();

  // インスタンスを取得する

  public static Calendar getInstance() {

    return create(“”);

  }

  // 指定されたカレンダーの種類に応じて、適切なカレンダーを生成する

  private static Calendar create(String type) {

    // このように、パラメーターに応じて生成するオブジェクトを変えるのが、「ファクトリーメソッド」である

    switch (type) {

    case “Japanese”:

      return new JapaneseCalendar(“JapaneseCalendar”);

    case “Chinese”:

      return new ChineseCalendar(“ChineseCalendar”);

    case “English”:

      return new BritishCalendar(“BritishCalendar”);

    default:

      return new JapaneseCalendar(“JapaneseCalendar”);

    }

  }

  // カレンダーの名前を設定する

  protected void setName(String name) {

    this.name = name;

  }

  // カレンダーの名前を取得する

  public String getName() {

    return name;

  }

}

// 日本用カレンダー

class JapaneseCalendar extends Calendar {

  JapaneseCalendar(String name) {

    setName(name);

  }

}

// 中国用カレンダー

class ChineseCalendar extends Calendar {

  ChineseCalendar(String name) {

    setName(name);

  }

}

// 英国用カレンダー

class BritishCalendar extends Calendar {

  BritishCalendar(String name) {

    setName(name);

  }

}

// Mainメソッド

public class Main {

  public static void main(String[] args) {

    // カレンダーオブジェクトを取得し、カレンダー名を表示する

    Calendar calendar = Calendar.getInstance();

    System.out.println(calendar.getName());

  }

}

 

//実行結果
JapaneseCalendar

非常におおざっぱではありますが、Calendarメソッドは上記のサンプルプログラムのように、内部で環境に合うカレンダーを作成しています。ただし、ファクトリーメソッドは難しい概念ではあるので、雰囲気だけ確認しておけば問題ないでしょう。

JavaのcompareToメソッドを使うときの注意点3選

JavaのcompareToメソッドを使うときの注意点3選

JavaのcompareToメソッドには下記3つの注意点があるので、ぜひ確認して実践にお役立てください。

  • どちらかが「null」の場合は例外が発生する
  • プリミティブ型はcompareToメソッドは使えない
  • ジェネリクスクラスのメソッドでは使えない

どちらかが「null」の場合は例外が発生する

compareToメソッドで比較する変数は、どちらか一方でも「null」の場合は、当然のことながら「NullPointerException」例外が発生します。NullPointerExceptionは、中身がnull(空)の変数にアクセスしたときに発生する例外です。下記のサンプルコードで、実際にnullの変数を比較してみましょう。

//サンプルプログラム
public class Main {

  public static void main(String[] args) {

    // 2つの変数に値を格納する

    Integer a = 1;

    Integer b = null;

    // 2つの値の大小比較を行う

    int result = a.compareTo(b);

    System.out.println(a + “と” + b + “の比較結果:” + result);

  }

}

 

//実行結果
Exception in thread “main” java.lang.NullPointerException: Cannot read field “value” because “anotherInteger” is null

at java.base/java.lang.Integer.compareTo(Integer.java:1477)

at Main.main(Main.java:9)

compareToを利用する場合は、あらかじめif文で「nullチェック」をしておくと安全です。また、仮にどちらかがnullだった場合は、たとえば文字列の場合は空文字として扱ったり、エラーメッセージを表示したりするほうが好ましいでしょう。nullチェックの一例を、下記のサンプルプログラムでご紹介します。

//サンプルプログラム
import java.util.Scanner;

public class Main {

  public static void main(String[] args) {

    // 2つの変数に値を格納する

    String a = input(1);

    String b = input(2);

    // 変数bをnullにする

    b = null;

    // 変数aのnullチェックを行う

    if (a == null) {

      // nullの場合は空文字列を代入する

      a = new String();

    }

    // 変数bのnullチェックを行う

    if (b == null) {

      // nullの場合は空文字列を代入する

      b = new String();

    }

    // 2つの値の大小比較を行う

    int result = a.compareTo(b);

    System.out.println(“「” + a + “」と「” + b + “」の比較結果:” + result);

    // 比較結果を基に詳細な結果を表示する

    if (result < 0) {

      System.out.println(“「” + a + “」は「” + b + “」より小さい”);

    } else if (result > 0) {

      System.out.println(“「” + a + “」は「” + b + “」より大きい”);

    } else {

      System.out.println(“「” + a + “」と「” + b + “」は等しい”);

    }

  }

  // キーボード入力を受け付けて、入力された値を返すメソッド

  // さまざまな結果を表示するためのメソッドなので、こちらの処理内容は無視してOKです

  public static String input(int count) {

    // キーボード入力を受け付けるScannerを生成する

    Scanner scanner = new Scanner(System.in);

    // 文字列の入力を受け付ける

    System.out.print(count + “つ目の文字列を入力してください:”);

    // 正常値を返す

    return scanner.next();

  }

}

 

//実行結果
1つ目の文字列を入力してください:TestA

2つ目の文字列を入力してください:TestB

「TestA」と「」の比較結果:5

「TestA」は「」より大きい

上記のサンプルプログラムでは、変数がnullの場合は単純に空文字列を代入していますが、厳密な動作が要求される場合は例外を発生させるのも良いでしょう。

プリミティブ型はcompareToメソッドは使えない

compareToメソッドはクラスのインスタンスに紐づいているため、クラスではないプリミティブ型では使えません。プリミティブ型を比較したい場合は、プリミティブ型に対応するIntegerやFloatなどラッパークラスの「compareメソッド」を使えば、compareToメソッドと同じことができます。

ただし、そもそもプリミティブ型は冒頭で紹介したように「比較演算子」が使えるので、ほとんどメリットはありません。詳細は下記のサンプルプログラムのとおりです。

//サンプルプログラム
import java.util.Scanner;

public class Main {

  public static void main(String[] args) {

    // 2つの変数に値を格納する

    int a = input(1);

    int b = input(2);

    // 2つの値の大小比較を行う

    int result = Integer.compare(a, b);

    System.out.println(a + “と” + b + “の比較結果:” + result);

    // 比較結果を基に詳細な結果を表示する

    if (result < 0) {

      System.out.println(a + “は” + b + “より小さい”);

    } else if (result > 0) {

      System.out.println(a + “は” + b + “より大きい”);

    } else {

      System.out.println(a + “と” + b + “は等しい”);

    }

  }

  // キーボード入力を受け付けて、入力された値を返すメソッド

  // さまざまな結果を表示するためのメソッドなので、こちらの処理内容は無視してOKです

  public static int input(int count) {

    // キーボード入力を受け付けるScannerを生成する

    Scanner scanner = new Scanner(System.in);

    // 整数値を受け付け、不正値の場合は再入力を求める

    System.out.print(count + “つ目の数値を入力してください:”);

    while (!scanner.hasNextInt()) {

      System.out.print(count + “つ目の数値を入力してください:”);

      scanner.next();

    }

    // 正常値を返す

    return scanner.nextInt();

  }

}

 

//実行結果
1つ目の数値を入力してください:100

2つ目の数値を入力してください:200

100と200の比較結果:-1

100は200より小さい

IntegerやFloatなどのラッパークラスには、staticメソッド「compare」が搭載されており、これを呼び出して2つの変数を引数に渡せば、compareToメソッドと同じ結果が得られます。ただし、わざわざcompareToメソッドを使わなくても、if-else文でも同じように比較できます。

ジェネリクスクラスのメソッドでは使えない

「class クラス名 <T>」の形式で作られる「ジェネリクスクラス」は、さまざまな型を受け付けることができます。ただし、compareToメソッドは「Comparable」なクラスでなければ使えないので、下記のようにジェネリクスクラスのメソッド内で、compareToメソッドを使うことはできません。

class Test<T> {

  public int compare(T x, T y) {

    return x.compareTo(y);

  }

}

上記のように書くと、「メソッドcompareTo(T)は型Tで未定義です」と表示されます。ジェネリクスクラスは、どのような型を入れても動作することが求められるので、特定のクラスに依存するcompareToメソッドは使えないのです。ただし、下記のように「型パラメータの制約」を行えば、問題なく使えるようになります。

//サンプルプログラム
// Comparableインターフェースを継承し、自作クラス「Test」を実装する

class Test<T extends Comparable<T>> {

  public int compare(T x, T y) {

    return x.compareTo(y);

  }

}

public class Main {

  public static void main(String[] args) {

    // テスト用クラスをインスタンス化する

    Test<Integer> test = new Test<Integer>();

    

    // 比較結果を表示する

    System.out.println(test.compare(1, 2));

    

    // ちなみに下記のように書くことはできない

    //Test<int> error = new Test<int>();

  }

}

 

//実行結果
-1

Testクラスの定義が「class Test<T extends Comparable<T>>」となっていることが理解いただけますでしょうか。この「T extends Comparable<T>」は、Comparableインターフェースを継承することを意味します。そもそもcompareToオブジェクトは、Comparableインターフェースを継承したクラスで、使うことができるメソッドなのです。

なお、サンプルプログラムの最後に記載しているように、TestクラスはComparableなオブジェクトしか入らないので、intやfloatのようなプリミティブ型は入れられません。一方で、IntegerやFloatなどのラッパークラスは格納できます。

【応用編】コレクションのソートには「Comparator」を使う

【応用編】コレクションのソートには「Comparator」を使う

配列やリストなどのコレクションをソートしたいときは、「Comparator」クラスを使うのがおすすめです。さらに、ComparatorはComparecompareToメソッドと組み合わせることにより、自作クラスもさまざまな方法でソートできるようになります。本章では応用編として、コレクションのソート方法を下記3つの観点から解説します。

  • Collecttionsクラスの「sortメソッド」第2引数にソート処理を記載する
  • 自作クラスのソートは「Comparableインターフェース」の継承で便利
  • 複雑な条件比較によるソートも「compareToメソッド」で簡単にできる

Collecttionsクラスの「sortメソッド」第2引数にソート処理を記載する

Comparatorは、ソートの条件を自分で指定するための「インターフェース」です。Collecttionsクラスの「sortメソッド」の第2引数でComparatorをインスタンス化し、「compare」メソッドを実装することにより、自由なソートができるようになります。詳細は下記のサンプルコードをご確認ください。

//サンプルプログラム
import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

import java.util.List;

public class Main {

  public static void main(String[] args) {

    // Integer型リストを生成する

    List<Integer> list = new ArrayList<>();

    // リストに要素を追加する

    list.add(5);

    list.add(1);

    list.add(7);

    list.add(4);

    list.add(2);

    list.add(6);

    list.add(3);

    // Comparatorインタフェースを実装し、Collections.sortメソッドでソートする

    Collections.sort(list, new Comparator<Integer>() {

      @Override

      public int compare(Integer left, Integer right) {

        // 降順ソートにする

        return -(left – right); // 降順なので符号を反転させる

      }

    });

    System.out.println(list);

  }

}

 

//実行結果
[7, 6, 5, 4, 3, 2, 1]

重要な部分は「Collections.sort」メソッドを呼び出している部分です。先ほどと同じように、Comparatorクラスをインスタンス化し、compareメソッドを実装しています。この部分の戻り値で、ソートの「大小比較」の方法を定義しているのです。その方法はcompareToメソッドと同じです。

compareToメソッドでは、戻り値は「左側 – 右側」の結果がマイナスになるとき、左側が右側より小さな値であることを示します。sortメソッド内部では「昇順」がデフォルトになっており、compareメソッドの戻り値が「プラス」の場合に並べ替えを行っています。つまり、降順ソートにしたい場合は、compareメソッドの戻り値を反転させれば良いのです。

ただし、上記のように降順ソートをするだけなら、Collections.sortメソッドの第2引数に「Collections.reverseOrder()」を渡せばOKです。

自作クラスのソートは「Comparableインターフェース」の継承で便利

Comparatorが本領を発揮するのは、下記のサンプルプログラムのように、自分で定義したクラスのリストなどを特定のフィールド(メンバ変数)の値を基準にソートできることです。ただし、自作クラスを定義する際は、できるだけComparableインターフェースを継承するようにすると、ソートが便利に使えるようになります。詳細は下記のサンプルプログラムでご確認ください。

//サンプルプログラム
import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

import java.util.List;

// Comparableインターフェースを継承し、自作クラス「Test」を実装する

class Test implements Comparable<Test> {

  // フィールドを定義する(ソート時のキーとなる)

  private int num;

  // 引数付きコンストラクタでフィールドを初期化する

  Test(int num) {

    this.num = num;

  }

  // 「compareToメソッド」をオーバーライドする

  @Override

  public int compareTo(Test other) {

    // 自身が比較対象より小さければマイナス、大きければプラスを返す

    if (this.num < other.num) {

      return -1;

    } else if (this.num > other.num) {

      return 1;

    } else {

      return 0;

    }

  }

  // printメソッドで表示するために「toStringメソッド」をオーバーライドする

  @Override

  public String toString() {

    return Integer.toString(num);

  }

}

// mainメソッド

public class Main {

  public static void main(String[] args) {

    // Integer型リストを生成する

    List<Test> list = new ArrayList<>();

    // リストに要素を追加する

    list.add(new Test(2));

    list.add(new Test(13));

    list.add(new Test(3));

    list.add(new Test(8));

    list.add(new Test(5));

    list.add(new Test(21));

    list.add(new Test(1));

    // Comparatorインタフェースを実装し、Collections.sortメソッドでソートする

    Collections.sort(list, new Comparator<Test>() {

      @Override

      public int compare(Test left, Test right) {

        // 降順ソートにする

        return -left.compareTo(right); // 降順ソートなので符号を反転させる

      }

    });

    System.out.println(list);

  }

}

 

//実行結果
[21, 13, 8, 5, 3, 2, 1]

自作の「Test」メソッドでは、スーパークラスのcompareToをオーバーライドしています。中身はこれまでのcompareToメソッドと同じです。また、printメソッドで表示するために「toStringメソッド」をオーバーライドしていることもポイントです。

mainメソッドのソート部分では、第2引数内部で自作のcompareToメソッドを呼び出し、さらに降順ソートにするため符号を反転させています。このような書き方に慣れておくと、より複雑な条件で自作クラスをソートしたいときに役立つでしょう。

複雑な条件比較によるソートも「compareToメソッド」で簡単にできる

より複雑な条件で自作クラスをソートしたい場合は、オーバーライドするcompareToメソッドの内部を変更するだけでOKです。なぜならソート部分自体では、「自作クラスのcompareToメソッドを呼び出すだけで良い」という点は変わらないからです。詳細を下記のサンプルプログラムで確認しましょう。

//サンプルプログラム
import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

import java.util.List;

// Comparableインターフェースを継承し、自作クラス「Test」を実装する

class Test implements Comparable<Test> {

  // フィールドを定義する(ソート時のキーとなる)

  private int num; // 番号

  private String name; // 氏名

  // 引数付きコンストラクタでフィールドを初期化する

  Test(int num, String name) {

    this.num = num;

    this.name = name;

  }

  // 「compareToメソッド」をオーバーライドする

  @Override

  public int compareTo(Test other) {

    // 今回のサンプルプログラムでは、基本的には名前を辞書式評価でソートする

    // そのため、まずはStringクラスのcompareToで比較し、

    // 同姓同名の場合は番号で比較し、自身が比較対象より小さければマイナス、大きければプラスを返す

    int result = this.name.compareTo(other.name);

    //同姓同名でなければ比較を終了する

    if (result != 0) {

      return result;

    }

    // 同姓同名の場合は、番号で比較する

    return Integer.compare(this.num, other.num);

  }

  // printメソッドで表示するために「toStringメソッド」をオーバーライドする

  @Override

  public String toString() {

    return “(氏名:” + name + “, ” + “番号:” + num + “)”;

  }

}

// mainメソッド

public class Main {

  public static void main(String[] args) {

    // Integer型リストを生成する

    List<Test> list = new ArrayList<>();

    // リストに要素を追加する

    list.add(new Test(2, “山田太郎”));

    list.add(new Test(13, “斎藤花子”));

    list.add(new Test(3, “佐々木次郎”));

    list.add(new Test(8, “鏑木美香”));

    list.add(new Test(5, “山田太郎”));

    list.add(new Test(21, “斎藤花子”));

    list.add(new Test(1, “山田太郎”));

    // Comparatorインタフェースを実装し、Collections.sortメソッドでソートする

    Collections.sort(list, new Comparator<Test>() {

      @Override

      public int compare(Test left, Test right) {

        // 降順ソートにする

        return -left.compareTo(right); // 降順ソートなので符号を反転させる

      }

    });

    System.out.println(list);

  }

}

 

//実行結果
[(氏名:鏑木美香, 番号:8), (氏名:斎藤花子, 番号:21), (氏名:斎藤花子, 番号:13), (氏名:山田太郎, 番号:5), (氏名:山田太郎, 番号:2), (氏名:山田太郎, 番号:1), (氏名:佐々木次郎, 番号:3)]

ご覧のように、Collections.sortメソッドの呼び出し部分は、先ほどのサンプルプログラムとまったく同じです。一方で、自作クラス「Test」に「name」というフィールド(メンバ変数)を追加し、compareToメソッド内部の比較方法を変更しています。比較方法についてはコメントで記載しているとおりで、name→numの順番で比較すると良いです。

実行結果では、山田太郎という人物は3人いますが、きちんと番号で降順ソートされていることがわかります。ComparableインターフェースとcompareToメソッドを組み合わせると、自作クラスでも簡単にソートできるようになるのでぜひお試しください。

Javaの「compareToメソッド」で値の比較を効率化しよう!

Javaの「compareToメソッド」で値の比較を効率化しよう!

Javaの「compareToメソッド」は、変数の値を簡単に比較できる便利なメソッドです。戻り値の扱い方には注意が必要ですが、基本的な整数や符号小数点数に加えて、文字列や時間なども比較できることが魅力です。

ただし、変数がnullの場合は例外が発生することや、ジェネリクスクラスで使用する場合はComparableインターフェースの継承が必要なことなど、いくつか注意点もあります。また、リストやMapなどをソートする場合も、compareToメソッドが役立ちます。

昇順ソートの場合はそのままの値を、降順ソートの場合は符号を反転させた値を返すようにすると、Collections.sortメソッドで簡単にソートできるでしょう。自作クラスをソートする場合は、ComparableインターフェースとcompareToメソッドを組み合わせると、複雑な条件でも簡単にソートできるようになるので便利です。

アクセスランキング 人気のある記事をピックアップ!

    コードカキタイがオススメする記事!

    1. 子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介

      2024.01.26

      子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介

      #プログラミングスクール

    2. 【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説

      2022.01.06

      【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説

      #プログラミングスクール

    3. 【未経験でも転職可】30代におすすめプログラミングスクール8選!

      2024.01.26

      【未経験でも転職可】30代におすすめプログラミングスクール8選!

      #プログラミングスクール

    4. 初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説

      2024.01.26

      初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説

      #JAVA

    5. 忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説

      2024.01.26

      忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説

      #プログラミングスクール

    1. 【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介

      2022.01.06

      【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介

      #プログラミングスクール

    2. 【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説

      2024.01.26

      【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説

      #プログラミングスクール

    3. 【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説

      2024.01.26

      【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説

      #プログラミングスクール

    4. 【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説

      2024.01.26

      【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説

      #プログラミングスクール

    5. 【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説

      2024.01.26

      【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説

      #プログラミングスクール