0
0

[備忘録 その15] Java Silver ~継承とポリモフィズム その4~

Last updated at Posted at 2024-08-14

ジェネリックス

ジェネリックスは、Javaにおいてクラスやメソッドに型引数を使用する機能です。ジェネリックスを使用することで、型の安全性を確保しつつ、再利用性の高いコードを記述できます。これにより、コンパイル時に型のチェックが行われ、不適切な型が使用されるのを防ぐことができます。

ジェネリックスの基本

ジェネリックスを使うと、クラスやメソッドを定義する際に、特定のデータ型に依存しないコードを書くことができます。例えば、リストのようなデータ構造において、ジェネリックスを使うことで、格納される要素の型を指定することができます。

ジェネリックスクラス

以下は、ジェネリッククラスの例です。Tは型引数(ジェネリック型)を表しており、任意の型を指定できます。

public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

Boxクラスは、Tというジェネリック型を使って定義されています。これにより、Boxクラスはさまざまな型のオブジェクトを扱えるようになります。

Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
System.out.println(stringBox.getItem());  // 出力: Hello

Box<Integer> intBox = new Box<>();
intBox.setItem(123);
System.out.println(intBox.getItem());  // 出力: 123

ここでは、BoxとBoxという具象型(型引数が具体的に指定された型)を使用しています。stringBoxには文字列を、intBoxには整数を格納しています。

ジェネリックスメソッド

ジェネリックスメソッドは、メソッド単位で型引数を使用する方法です。メソッドの戻り値や引数の型にジェネリックスを使用できます。

public class Utils {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }
}

この例では、printArrayメソッドがジェネリックメソッドとして定義されています。はメソッドの型引数を表し、T[]は任意の型の配列を受け取ることができます。

String[] stringArray = {"Apple", "Banana", "Cherry"};
Integer[] intArray = {1, 2, 3};

Utils.printArray(stringArray);
Utils.printArray(intArray);

ワイルドカード

ジェネリックスには、ワイルドカード(?)を使って、型引数に柔軟性を持たせることもできます。ワイルドカードには主に以下の3種類があります。
1. 無制限ワイルドカード(?):

  • 任意の型を受け入りることができます。
public void printList(List<?> list) {
    for (Object element : list) {
        System.out.println(element);
    }
}

2. 境界付きワイルドカード(上限付き? extends T):

  • 指定された型Tまたはそのサブクラスを受け入れることができます。
public void printNumbers(List<? extends Number> list) {
    for (Number number : list) {
        System.out.println(number);
    }
}

3. 境界付きワイルドカード(下限付き ? super T):

  • 指定された型Tまたはそのスーパークラスを受け入れることができます。
public void addNumbers(List<? super Integer> list) {
    list.add(10);
    list.add(20);
}

オブジェクトの順序付け

Javaでオブジェクトの順序付けを行うには、ComparableインターフェースやComparatorインターフェースを使用します。これらを利用することで、オブジェクトの自然順序付けやカスタムの順序付けを定義できます。

1. Comparableインターフェース

Comparableインターフェースは、オブジェクトが自然順序付けを持つことを示します。このインターフェースは、オブジェクト自身がほかのオブジェクトと比較される方法を定義します。Comparableインターフェースを実装するクラスは、compareToメソッドをオーバーライドする必要があります。
compareToメソッドの使い方

  • compareTo(T o)メソッドは、以下の値を返します。:
    • 負の整数:このオブジェクトが指定されたオブジェクトよりも小さい場合
    • 0 : このオブジェクトが指定されたオブジェクトと等しい場合
    • 正の整数:このオブジェクトが指定されたオブジェクトよりも大きい場合
public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Person other) {
        return Integer.compare(this.age, other.age);
    }

    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

上記の例では、PersonクラスはComparableを実装しており、年齢でオブジェクトを比較します。この場合、compareToメソッドはthis.ageとother.ageを比較し、順序を決定します。

List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 30));
people.add(new Person("Bob", 25));
people.add(new Person("Charlie", 35));

Collections.sort(people);

for (Person person : people) {
    System.out.println(person);
}
// 出力: 
// Bob (25)
// Alice (30)
// Charlie (35)

2. Comparatorインターフェース

Comparatorインターフェースは、オブジェクトのカスタム付けを定義するために使用されます。このインターフェースは、クラスの自然順序とは異なる順序でオブジェクトをソートしたい場合に便利です。Comparatorを実装するクラスはcompareメソッドをオーバーライドする必要があります。

compareメソッドの使い方

  • compare(T o1,T o2)メソッドは、以下の値を返します:
    • 負の整数:o1がo2よりも小さい場合
    • 0:o1がo2と等しい場合
    • 正の整数:o1がo2よりも大きい場合
public class NameComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.name.compareTo(p2.name);
    }
}

上記の例では、NameComparatorクラスはComparatorを実装しており、名前でPersonオブジェクトを比較します。

Collections.sort(people, new NameComparator());

for (Person person : people) {
    System.out.println(person);
}
// 出力:
// Alice (30)
// Bob (25)
// Charlie (35)

3. Comparatorとラムダ式

Java 8以降、Comparatorインターフェースはラムダ式で簡単に記述できます。例えば、年齢で逆順にソートするには、以下のように記述できます。

Collections.sort(people, (p1, p2) -> Integer.compare(p2.age, p1.age));

for (Person person : people) {
    System.out.println(person);
}
// 出力:
// Charlie (35)
// Alice (30)
// Bob (25)

4. メソッド参照とComparatorの組み合わせ

さらに、Comparatorをメソッド参照と組み合わせて使用することもできます。例えば、PersonクラスにgetNameメソッドがある場合、それを使って名前順にソートできます。

Collections.sort(people, Comparator.comparing(Person::getName));

配列とリストのソートと検索

1.配列のソートと検索

配列のソート

Javaでは、Arraysクラスを使用して配列をソートできます。配列をソートするには、Arrays.sort()メソッド使用します。これは、配列の要素を昇順に並べ替えます。

  • 整数配列のソート:
int[] numbers = {5, 3, 8, 1, 2};
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers));  // 出力: [1, 2, 3, 5, 8]
  • 文字列配列のソート:
String[] names = {"Charlie", "Alice", "Bob"};
Arrays.sort(names);
System.out.println(Arrays.toString(names));  // 出力: [Alice, Bob, Charlie]

配列の検索

配列の中から特定の要素を検索するには、Arrays.binarySearch()メソッドを使用します。注意点として、binarySearchメソッドを使用するには、まず配列がソートされている必要があります。

  • 整数配列の検索
int[] numbers = {1, 2, 3, 5, 8};
int index = Arrays.binarySearch(numbers, 5);
System.out.println(index);  // 出力: 3
  • 文字列配列の検索
String[] names = {"Alice", "Bob", "Charlie"};
int index = Arrays.binarySearch(names, "Bob");
System.out.println(index);  // 出力: 1

もし、配列にその要素が存在しない場合、binarySearchメソッドは負の値を返します。この値は、要素がどの位置に挿入されるべきかを示します。

2.リストのソートと検索

リストのソート

Javaでは、Collectionsクラスを使用してリストをソートできます。リストをソートするには、Collections.sort()メソッドを使用します。これにより、リスト内の要素が昇順に並べ替えられます。

  • 整数リストのソート:
List<Integer> numbers = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
Collections.sort(numbers);
System.out.println(numbers);  // 出力: [1, 2, 3, 5, 8]
  • 文字列リストのソート:
List<String> names = new ArrayList<>(Arrays.asList("Charlie", "Alice", "Bob"));
Collections.sort(names);
System.out.println(names);  // 出力: [Alice, Bob, Charlie]

リストの検索

リストの中から特定の要素を検索するには、Collections.binarySearch()メソッドを使用します。配列と同様に、リストも検索する前にソートされている必要があります。

  • 整数リストの検索:
List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 8);
int index = Collections.binarySearch(numbers, 5);
System.out.println(index);  // 出力: 3
  • 文字列リストの検索:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
int index = Collections.binarySearch(names, "Bob");
System.out.println(index);  // 出力: 1

リスト内に検索対象の要素のない場合も、binarySearchメソッドは負の値を返し、その値は要素が挿入されるべき位置を示します。

3.比較のためのカスタムロジック

デフォルトのソート方法(自然順序付け)に加えて、カスタムの比較ロジックを指定してソートすることも可能です。例えば、オブジェクトの特定のフィールドを基準にソートする場合などです。Comparatorインターフェースを使ってカスタムのソート順を指定できます。

  • カスタムソート
List<Person> people = new ArrayList<>();
people.add(new Person("Charlie", 30));
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 35));

// 年齢でソート
Collections.sort(people, Comparator.comparing(Person::getAge));

for (Person person : people) {
    System.out.println(person.getName() + " (" + person.getAge() + ")");
}
// 出力:
// Alice (25)
// Charlie (30)
// Bob (35)

まとめ

  • 配列のソートと検索: Arrays.sort()でソートし、Arrays.binarySearch()で検索する。

  • リストのソートと検索: Collections.sort()でソートし、Collections.binarySearch()で検索する。

  • カスタムソート: Comparatorを使用して、カスタムロジックでソート可能。

コレクションとラムダ式について

1.コレクションの基本

コレクションフレームワークは、複数のデータを一括して扱うためのデータ構造を提供します。Javaのコレクションは、主に以下のようなインタフェースとクラスで構成されています。

主なコレクションインタフェース

1. List: 順序を持ち、重複を許可するコレクションです。インデックスによるアクセスが可能です。

  • 実装クラス: ArrayList, LinkedList, Vector

2. Set: 順序を持たず、重複を許可しないコレクションです。

  • 実装クラス: HashSet, LinkedHashSet, TreeSet

3. Map: キーと値のペアを保持するコレクションで、キーは一意でなければなりません。

  • 実装クラス: HashMap, LinkedHashMap, TreeMap

4. Queue: 要素をFIFO順で保持するコレクションです。

  • 実装クラス: PriorityQueue, LinkedList

コレクションの基本操作

List<String> list = new ArrayList<>();
list.add("Alice");
list.add("Bob");
list.add("Charlie");

System.out.println(list.get(1));  // 出力: Bob

Set<String> set = new HashSet<>();
set.add("Alice");
set.add("Bob");
set.add("Alice");  // 重複する要素は無視される

System.out.println(set.size());  // 出力: 2

Map<String, Integer> map = new HashMap<>();
map.put("Alice", 30);
map.put("Bob", 25);

System.out.println(map.get("Bob"));  // 出力: 25

2.ラムダ式の基本

Java 8で導入されたラムダ式は、匿名関数のように使用される簡潔なコードブロックです。ラムダ式を使用することで、インターフェースを匿名クラスとして実装する手間が省け、コードが読みやすくなります。

ラムダ式の構文:

基本構文は以下の通りです:

(引数リスト) -> { 関数の本体 }
  • 引数リストはカッコ内に記述し、引数が一つの場合はカッコを省略できます。
  • 関数の本体が単一のステートメントである場合、波括弧も省略できます。

ラムダ式の例

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// ラムダ式を使用してリストの各要素を出力する
names.forEach(name -> System.out.println(name));

// ラムダ式を使用してリストをソートする
names.sort((name1, name2) -> name1.compareTo(name2));
System.out.println(names);  // 出力: [Alice, Bob, Charlie]

3.コレクションとラムダ式の組み合わせ

ラムダ式はコレクションの操作で非常に強力です。例えば、forEach、filter、map、reduceなどの操作をラムダ式と組み合わせることで、コレクションのデータを簡潔に操作できます。

例: フィルタリングとマッピング

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

// 名前が"B"で始まるものをフィルタリングし、大文字に変換して出力する
names.stream()
    .filter(name -> name.startsWith("B"))
    .map(name -> name.toUpperCase())
    .forEach(name -> System.out.println(name));
// 出力: BOB

例: ソートと比較

Comparatorとラムダ式を組み合わせることで、リストを簡単にソートできます。

List<Person> people = new ArrayList<>();
people.add(new Person("Charlie", 30));
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 35));

// 年齢でソート
people.sort((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()));

for (Person person : people) {
    System.out.println(person.getName() + " (" + person.getAge() + ")");
}
// 出力:
// Alice (25)
// Charlie (30)
// Bob (35)

最初のページ
[備忘録 その1] Java Silver ~Javaプログラミング基礎~

前のページ
[備忘録 その14] Java Silver ~継承とポリモフィズム その3~

次のページ
[備忘録 その16] Java Silver ~例外~

0
0
0

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
0
0