[備忘録 その１５] Java Silver ～継承とポリモフィズム その４～

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

## ジェネリックスの基本
ジェネリックスを使うと、クラスやメソッドを定義する際に、特定のデータ型に依存しないコードを書くことができます。例えば、リストのようなデータ構造において、ジェネリックスを使うことで、格納される要素の型を指定することができます。
#### ジェネリックスクラス
以下は、ジェネリッククラスの例です。Tは型引数（ジェネリック型）を表しており、任意の型を指定できます。
```java
public class Box<T> {
    private T item;

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

    public T getItem() {
        return item;
    }
}

```
Boxクラスは、Tというジェネリック型を使って定義されています。これにより、Boxクラスはさまざまな型のオブジェクトを扱えるようになります。
```java
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<String>とBox<Integer>という具象型（型引数が具体的に指定された型）を使用しています。stringBoxには文字列を、intBoxには整数を格納しています。

#### ジェネリックスメソッド
ジェネリックスメソッドは、メソッド単位で型引数を使用する方法です。メソッドの戻り値や引数の型にジェネリックスを使用できます。
```java
public class Utils {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }
}
```
この例では、printArrayメソッドがジェネリックメソッドとして定義されています。<T>はメソッドの型引数を表し、T[]は任意の型の配列を受け取ることができます。
```java
String[] stringArray = {"Apple", "Banana", "Cherry"};
Integer[] intArray = {1, 2, 3};

Utils.printArray(stringArray);
Utils.printArray(intArray);
```
#### ワイルドカード
ジェネリックスには、ワイルドカード(？)を使って、型引数に柔軟性を持たせることもできます。ワイルドカードには主に以下の3種類があります。
__1\. 無制限ワイルドカード(？):__
* 任意の型を受け入りることができます。
```java
public void printList(List<?> list) {
    for (Object element : list) {
        System.out.println(element);
    }
}
```

__2\. 境界付きワイルドカード(上限付き？ extends T):__
* 指定された型Tまたはそのサブクラスを受け入れることができます。
```java
public void printNumbers(List<? extends Number> list) {
    for (Number number : list) {
        System.out.println(number);
    }
}
```


__3\. 境界付きワイルドカード（下限付き ? super T）:__
* 指定された型Tまたはそのスーパークラスを受け入れることができます。
```java
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 ： このオブジェクトが指定されたオブジェクトと等しい場合
    * 正の整数：このオブジェクトが指定されたオブジェクトよりも大きい場合
```java
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を比較し、順序を決定します。
```java
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よりも大きい場合
```java
public class NameComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.name.compareTo(p2.name);
    }
}
```
上記の例では、NameComparatorクラスはComparatorを実装しており、名前でPersonオブジェクトを比較します。
```java
Collections.sort(people, new NameComparator());

for (Person person : people) {
    System.out.println(person);
}
// 出力:
// Alice (30)
// Bob (25)
// Charlie (35)
```
## 3\. Comparatorとラムダ式
Java 8以降、Comparatorインターフェースはラムダ式で簡単に記述できます。例えば、年齢で逆順にソートするには、以下のように記述できます。
```java
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メソッドがある場合、それを使って名前順にソートできます。
```java
Collections.sort(people, Comparator.comparing(Person::getName));
```

# 配列とリストのソートと検索
## 1\.配列のソートと検索
#### 配列のソート
Javaでは、Arraysクラスを使用して配列をソートできます。配列をソートするには、Arrays.sort()メソッド使用します。これは、配列の要素を昇順に並べ替えます。
* __整数配列のソート：__
```java
int[] numbers = {5, 3, 8, 1, 2};
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers));  // 出力: [1, 2, 3, 5, 8]
```

* __文字列配列のソート：__
```java
String[] names = {"Charlie", "Alice", "Bob"};
Arrays.sort(names);
System.out.println(Arrays.toString(names));  // 出力: [Alice, Bob, Charlie]
```
#### 配列の検索
配列の中から特定の要素を検索するには、Arrays.binarySearch()メソッドを使用します。注意点として、binarySearchメソッドを使用するには、まず配列がソートされている必要があります。
* __整数配列の検索__
```java
int[] numbers = {1, 2, 3, 5, 8};
int index = Arrays.binarySearch(numbers, 5);
System.out.println(index);  // 出力: 3
```

* __文字列配列の検索__
```java
String[] names = {"Alice", "Bob", "Charlie"};
int index = Arrays.binarySearch(names, "Bob");
System.out.println(index);  // 出力: 1
```
もし、配列にその要素が存在しない場合、binarySearchメソッドは負の値を返します。この値は、要素がどの位置に挿入されるべきかを示します。
## 2\.リストのソートと検索
#### リストのソート
Javaでは、Collectionsクラスを使用してリストをソートできます。リストをソートするには、Collections.sort()メソッドを使用します。これにより、リスト内の要素が昇順に並べ替えられます。

* __整数リストのソート:__
```java
List<Integer> numbers = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
Collections.sort(numbers);
System.out.println(numbers);  // 出力: [1, 2, 3, 5, 8]
```

* __文字列リストのソート:__
```java
List<String> names = new ArrayList<>(Arrays.asList("Charlie", "Alice", "Bob"));
Collections.sort(names);
System.out.println(names);  // 出力: [Alice, Bob, Charlie]
```
#### リストの検索
リストの中から特定の要素を検索するには、Collections.binarySearch()メソッドを使用します。配列と同様に、リストも検索する前にソートされている必要があります。
* __整数リストの検索:__
```java
List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 8);
int index = Collections.binarySearch(numbers, 5);
System.out.println(index);  // 出力: 3
```

* __文字列リストの検索:__
```java
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
int index = Collections.binarySearch(names, "Bob");
System.out.println(index);  // 出力: 1
```
リスト内に検索対象の要素のない場合も、binarySearchメソッドは負の値を返し、その値は要素が挿入されるべき位置を示します。

## 3\.比較のためのカスタムロジック
デフォルトのソート方法(自然順序付け)に加えて、カスタムの比較ロジックを指定してソートすることも可能です。例えば、オブジェクトの特定のフィールドを基準にソートする場合などです。Comparatorインターフェースを使ってカスタムのソート順を指定できます。
* __カスタムソート__
```java
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
#### コレクションの基本操作
```java
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で導入されたラムダ式は、匿名関数のように使用される簡潔なコードブロックです。ラムダ式を使用することで、インターフェースを匿名クラスとして実装する手間が省け、コードが読みやすくなります。
#### ラムダ式の構文：
基本構文は以下の通りです：
```java
(引数リスト) -> { 関数の本体 }
```
* 引数リストはカッコ内に記述し、引数が一つの場合はカッコを省略できます。
* 関数の本体が単一のステートメントである場合、波括弧も省略できます。
#### ラムダ式の例
```java
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などの操作をラムダ式と組み合わせることで、コレクションのデータを簡潔に操作できます。

#### 例: フィルタリングとマッピング
```java
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とラムダ式を組み合わせることで、リストを簡単にソートできます。
```java
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)
```

最初のページ
[[備忘録 その１] Java Silver ～Javaプログラミング基礎～](https://qiita.com/MagicShowTime/items/9fcf3a2cb4a7162aafcf "[備忘録 その１] Java Silver ～Javaプログラミング基礎～")

前のページ
[[備忘録 その１４] Java Silver ～継承とポリモフィズム その３～](https://qiita.com/MagicShowTime/items/5ed80b2f799c2ae48033 "[備忘録 その１４] Java Silver ～継承とポリモフィズム その３～")

次のページ
[[備忘録 その１６] Java Silver ～例外～](https://qiita.com/MagicShowTime/items/ec5beac17041b7fa47e9 "[備忘録 その１６] Java Silver ～例外～")