Java 逆引き集 | Arrays クラスユーティリティ(sort / binarySearch) — 配列操作

Java Java
スポンサーリンク

Arrays クラスユーティリティ(sort / binarySearch) — 配列操作

配列の並べ替えと検索は「まずソートしてから二分探索」が基本。Java の java.util.Arrays は、配列向けに高速な sort と正確な binarySearch を用意しています。初心者でも迷わない使い方、落とし穴、テンプレートをまとめます。


配列の前提と基本

  • 固定長のデータ構造: 配列は作成後に長さを変えられません(要素の追加・削除は不可)。サイズやインデックスの扱いを理解した上で、内容の並び替え・検索を行います。
  • 配列の型ごとのメソッド: Arrays.sort はプリミティブ配列(int[] など)と参照型配列(String[] など)でオーバーロードされており、どちらも簡単に使えます。

Arrays.sort(並べ替え)

基本の昇順ソート

import java.util.Arrays;

public class SortBasics {
    public static void main(String[] args) {
        int[] numbers = {5, 3, 8, 1};
        Arrays.sort(numbers); // 昇順に並べ替え
        System.out.println(Arrays.toString(numbers)); // [1, 3, 5, 8]
    }
}
Java
  • ポイント: 昇順の「自然順序」で並べ替え。文字列なら辞書順、数値なら小さい順になります。

降順ソート(参照型のみ)

String[] words = {"cat", "apple", "hi", "banana"};
Arrays.sort(words, java.util.Comparator.reverseOrder());
System.out.println(Arrays.toString(words)); // [hi, cat, banana, apple]
Java
  • 注意: 降順は Comparator を渡せる参照型配列で可能。プリミティブ配列は一度ラップ(Integer[] など)するか、昇順ソート後に自前で反転します。

カスタム比較(オブジェクト配列)

class User {
    final String name;
    final int age;
    User(String name, int age) { this.name = name; this.age = age; }
    public String toString() { return name + "(" + age + ")"; }
}

User[] users = {
    new User("Tanaka", 30), new User("Sato", 22), new User("Kato", 22)
};

// 年齢昇順、同年齢は名前昇順
java.util.Arrays.sort(users,
    java.util.Comparator.comparingInt((User u) -> u.age)
                        .thenComparing(u -> u.name));
System.out.println(java.util.Arrays.toString(users));
// [Kato(22), Sato(22), Tanaka(30)]
Java
  • コツ: comparingthenComparing を使うと「主キー→副キー」の並びルールを読みやすく書けます。

Arrays.binarySearch(二分探索)

基本の使い方(見つかったらインデックス、なければ負の値)

import java.util.Arrays;

int[] sorted = {1, 3, 5, 7, 9, 11};
int idx1 = Arrays.binarySearch(sorted, 7);   // 3(見つかった位置)
int idx2 = Arrays.binarySearch(sorted, 8);   // 負の値(挿入位置規則)

System.out.println(idx1); // 3
System.out.println(idx2); // -5 など(実行環境で同じ)
Java
  • 前提: 配列は事前にソート済みである必要があります。未ソートのまま binarySearch を使うと、結果が意図通りになりません。
  • 返り値の規則: 要素が見つかった場合はそのインデックス。見つからない場合は「挿入すべき位置」を -(insertionPoint) - 1 の形で返します(負の値)。この規則により、戻り値から「どこに挿入すればよいか」を復元できます。
  • 性能: 二分探索は配列長を毎回半分に絞るため、計算量はおおむね O(log n)。大規模データでも高速です。

使いこなしテンプレート(挿入位置を求める)

int pos = Arrays.binarySearch(sorted, target);
int insertionPoint = (pos >= 0) ? pos : -pos - 1; // ここに挿入すると順序が保てる
Java
  • 応用: ソート済み配列を保ったまま挿入位置を計算できます(ただし配列は固定長なので、挿入自体は別の構造が必要)。

文字列配列でも同様

String[] names = {"Alice", "Bob", "Carol", "Dave"}; // 昇順
int idx = Arrays.binarySearch(names, "Carol"); // 2
Java
  • 注意: 比較は自然順序。ロケールに配慮した文字列比較が必要なら、ソート時点で Collator を使うなどの工夫を行います。

よくある落とし穴と回避策

  • 未ソートでの検索: ソートせずに binarySearch → 誤結果。
    • 回避: 必ず Arrays.sort 実行後に binarySearch
  • プリミティブ降順の比較指定不可:int[]Comparator は渡せません。
    • 回避: Integer[] に変換してソート、または昇順後に反転。
  • 挿入位置の負値規則を誤解: 負値は単なる「見つからない」ではなく「挿入位置情報」。
    • 回避: insertionPoint = -pos - 1 を必ず適用。
  • 固定長ゆえの挿入不可: 配列は長さ変更できない。
    • 回避: 追加・挿入が必要なら ArrayList に移行、または Arrays.copyOf でサイズを増やして末尾へ追加。

便利ユーティリティ(覚えておくと楽)

  • 配列の文字列化: Arrays.toString(arr) / 多次元は Arrays.deepToString(arr2d)
  • サイズ変更コピー: Arrays.copyOf(arr, newLen) / 範囲コピーは Arrays.copyOfRange(arr, from, to)
  • 等価性・ハッシュ: 多次元は Arrays.deepEquals / Arrays.deepHashCode

例題で身につける

例題1: 点数を昇順に並べてターゲットを高速検索

int[] scores = {72, 88, 90, 65, 99, 80};
Arrays.sort(scores); // [65, 72, 80, 88, 90, 99]
int pos = Arrays.binarySearch(scores, 88); // 3
System.out.println(pos >= 0 ? "見つかった: index=" + pos : "未検出");
Java
  • ねらい: ソート→二分探索の基本パターンを体得。

例題2: 文字列配列の検索と挿入位置算出

String[] cities = {"Chiba", "Kyoto", "Osaka", "Tokyo"};
Arrays.sort(cities); // [Chiba, Kyoto, Osaka, Tokyo]
int pos = Arrays.binarySearch(cities, "Kobe"); // 未検出 → 負の値
int ins = (pos >= 0) ? pos : -pos - 1;         // 挿入位置
System.out.println("Kobe の挿入位置 = " + ins);
Java
  • ねらい: 負値規則から挿入位置を導く練習。

例題3: オブジェクト配列の複合キーソート

class Product {
    final String name; final int price;
    Product(String n, int p) { name = n; price = p; }
    public String toString(){ return name + "(" + price + ")"; }
}
Product[] ps = { new Product("Tea", 300), new Product("Coffee", 500), new Product("Cake", 500) };

Arrays.sort(ps,
    java.util.Comparator.comparingInt((Product p) -> p.price) // 価格昇順
                        .thenComparing(p -> p.name));          // 同価格は名前昇順
System.out.println(Arrays.toString(ps)); // [Tea(300), Cake(500), Coffee(500)]
Java
  • ねらい: Comparator で業務ロジックに沿った並び替え。

テンプレート集

  • 昇順ソート(プリミティブ)
Arrays.sort(arr);
Java
  • 降順ソート(参照型)
Arrays.sort(arr, Comparator.reverseOrder());
Java
  • 二分探索+挿入位置
int pos = Arrays.binarySearch(arr, key);
int insertionPoint = (pos >= 0) ? pos : -pos - 1;
Java
  • コピー/範囲コピー
T[] copy = Arrays.copyOf(arr, newLen);
T[] part = Arrays.copyOfRange(arr, from, to); // [from, to)
Java

まとめ

  • 配列の検索は「ソートしてから binarySearch」が基本。見つからないときの返り値は「挿入位置を表す負値」なので必ず解釈する。sort はプリミティブも参照型も対応、参照型は Comparator で柔軟に並べ替えられる。配列は固定長なので、挿入・追加が必要ならコピーやリストへ移行するのが現実的です。
タイトルとURLをコピーしました