Java, Groovy, Kotlinにおけるコレクション操作の比較

# 概要

Java、Groovy、Kotlin は JVM ベースの言語で、それぞれがコレクション操作で独特な特徴を持っています。
今回は、これらの言語がコレクション操作にどのような特徴を持つかを、簡単なサンプルコードを交えて見ていきましょう。
また、各言語がコレクションにどんな特性を持っているかも軽く触れてみます。

# 各言語におけるコレクションの特徴

## Java

- ミュータブル（変更可能）なコレクションを主に使用
- `java.util`パッケージ内のコレクションクラス（`ArrayList`, `HashSet` など）
- Stream API を用いて、関数型のアプローチでコレクション操作が可能

## Groovy

- Java のコレクションに追加的な便利メソッドを提供
- ミュータブルおよびイミュータブルの両方のコレクション操作をサポート
- `collect`, `findAll` など、簡単で強力なメソッドが特徴

## Kotlin

- デフォルトでイミュータブル（変更不可能）なコレクションを提供
- ミュータブルなコレクションも利用可能（`mutableListOf`, `mutableSetOf` など）
- より安全で宣言的なコレクション操作をサポート

# コレクション操作の比較

Groovy と Kotlin では、コレクションに対する操作が直接かつ容易に行えます。これらの言語では、関数型プログラミングの要素が強く反映されており、コレクションを直感的に扱うための豊富なメソッドが提供されています。Groovy は Java のコレクションに対しても、簡単にアクセスし操作できるメソッドを多数提供しています。

一方で、Java ではコレクションを効率的に操作するためには、それらを Stream API に変換する必要があります。Stream API は、中間操作と終端操作の二つのカテゴリに分かれており、中間操作ではストリームを変換し新しいストリームを生成し、終端操作では最終的な処理結果を出力します。このように、Java では Stream を介して複雑なデータ操作を簡潔で読みやすいコードで実現することができます。

## Stream API 中間操作（Intermediate Operations）

| 操作                 | Java Stream API | Groovy          | Kotlin        |
| -------------------- | --------------- | --------------- | ------------- |
| フィルタリング       | `filter()`      | `findAll()`     | `filter()`    |
| 変換処理             | `map()`         | `collect()`     | `map()`       |
| 多層データの平坦化   | `flatMap()`     | `collectMany()` | `flatMap()`   |
| 限定取得             | `limit()`       | `take()`        | `take()`      |
| 条件付き取得         | `takeWhile()`   | -               | `takeWhile()` |
| 条件付き削除         | `dropWhile()`   | -               | `dropWhile()` |
| 重複の削減           | `distinct()`    | `unique()`      | `distinct()`  |
| ソート               | `sorted()`      | `sort()`        | `sorted()`    |
| 各要素へのアクション | `peek()`        | `each()`        | -             |

## Stream API 終端操作（Terminal Operations）

| 操作                     | Java Stream API                                                                                | Groovy                           | Kotlin                           |
| ------------------------ | ---------------------------------------------------------------------------------------------- | -------------------------------- | -------------------------------- |
| 要素の統合               | `reduce()`                                                                                     | `inject()`, `sum()`              | `reduce()`, `fold()`             |
| 最小・最大値の検索       | `min()`, `max()`                                                                               | `min()`, `max()`                 | `minOrNull()`, `maxOrNull()`     |
| 条件に合致する要素の確認 | `anyMatch()`, `allMatch()`, `noneMatch()`                                                      | `any()`, `every()`, `none()`     | `any()`, `all()`, `none()`       |
| 最初の要素の検索         | `findFirst()`, `findAny()`                                                                     | `find()`                         | `find()`, `firstOrNull()`        |
| 要素数のカウント         | `count()`                                                                                      | `count()`                        | `count()`                        |
| 要素の操作               | `forEach()`                                                                                    | `each()`                         | `forEach()`                      |
| グループ化               | `Collectors.groupingBy()`                                                                      | `groupBy()`                      | `groupBy()`                      |
| 分割                     | `Collectors.partitioningBy()`                                                                  | -                                | -                                |
| コレクション変換         | `Collectors.toList()`, `Collectors.toSet()`, `Collectors.toMap()`, `Collectors.toCollection()` | `toList()`, `toSet()`, `toMap()` | `toList()`, `toSet()`, `toMap()` |
| 平均の計算               | `Collectors.averagingInt()`, `Collectors.averagingLong()`, `Collectors.averagingDouble()`      | `average()`                      | `average()`                      |
| 文字列結合               | `Collectors.joining()`                                                                         | `join()`                         | `joinToString()`                 |

# サンプルコード

各言語でのコレクション操作のサンプルコードを示します。

## Java のサンプル

```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class JavaSample {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("Java", "Groovy", "Kotlin");

        // フィルタリングとマッピング
        List<String> filtered = words.stream()
                                     .filter(s -> s.startsWith("J"))
                                     .map(String::toUpperCase)
                                     .collect(Collectors.toList());

        System.out.println(filtered); // [JAVA]
    }
}
```

## Groovy のサンプル

```groovy
def words = ["Java", "Groovy", "Kotlin"]

// フィルタリングとマッピング
def filtered = words.findAll { it.startsWith("J") }
                    .collect { it.toUpperCase() }

println filtered // [JAVA]
```

## Kotlin のサンプル

```kotlin
fun main() {
    val words = listOf("Java", "Groovy", "Kotlin")

    // フィルタリングとマッピング
    val filtered = words.filter { it.startsWith("J") }
                        .map { it.toUpperCase() }

    println(filtered) // [JAVA]
}
```

# 最後に

Java、Groovy、Kotlin はそれぞれ独自のコレクション操作メソッドを提供しています。
特に Groovy と Kotlin は、Java の Stream API を利用することも可能ですが、それぞれの言語に最適化されたメソッドを活用することで、コードの効率性や可読性が向上します。
各言語の機能を活かし、適切なプログラミングを行いましょう。