Java Streams: A Beginner's Guide
こんにちは、将来のJavaの魔法使い们!今日は、Java Streamsの世界に興味深く飛び込んでみましょう。プログラミングが初めてであれば心配しないでください。私はあなたの親切なガイドとして、一歩一歩進んでいきます。このチュートリアルの終わりには、プロのようにデータをストリーミングできるようになるでしょう!
JavaでのStreamとは?
Conveyor belt sushi restaurantを思い浮かべてください。寿司のプレートがあなたの前を絶えず流れていき、あなたは好みのものを選んで食べられます。これがJavaでのStreamの概念です。一時的にメモリに保存することなく、要素を一つずつ処理することができます。
Streamは、Java 8で導入され、コレクションデータをより簡単かつ効率的に操作するためのものです。これにより、データに対する操作をより宣言的な方法で行うことができ、Javaに何をしたいかを伝えるだけで済みます。
JavaでStreamを生成する
まず、最初のStreamを作成してみましょう。いくつかの方法がありますが、簡単な例から始めます:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) {
// ListからStreamを作成
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");
Stream<String> fruitStream = fruits.stream();
// 直接Streamを作成
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
}
}
この例では、2つのStreamを作成しています。まずは、フルーツのListから作成したfruitStream
、次に直接作成したnumberStream
です。
一般的なStream操作
Streamを取得したので、それにどのような操作を行えるか見てみましょう。
forEachメソッド
forEach
メソッドは、Streamの各要素を処理するロボットのようです。まずはフルーツを印刷してみます:
fruits.stream().forEach(fruit -> System.out.println(fruit));
これは以下のように印刷します:
apple
banana
cherry
date
ここでfruit -> System.out.println(fruit)
はラムダ式と呼ばれ、各要素に対して何をすべきかをJavaに短く伝える方法です。
mapメソッド
map
メソッドは、Streamの各要素を変換する魔法の杖のようです。すべてのフルーツを大文字にしてみます:
List<String> upperCaseFruits = fruits.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCaseFruits);
これは以下のように印刷します:
[APPLE, BANANA, CHERRY, DATE]
String::toUpperCase
はメソッド参照と呼ばれ、各Stringに対してtoUpperCase
メソッドを使用するという意味です。
filterメソッド
filter
メソッドは、ナイトクラブのボーイのように特定の要素だけを通過させるものです。'A'で始まるフルーツだけを残してみます:
List<String> aFruits = fruits.stream()
.filter(fruit -> fruit.startsWith("a"))
.collect(Collectors.toList());
System.out.println(aFruits);
これは以下のように印刷します:
[apple]
limitメソッド
limit
メソッドは「これだけありがとう!」という感じで、Streamを特定の要素数に制限します:
List<String> firstTwoFruits = fruits.stream()
.limit(2)
.collect(Collectors.toList());
System.out.println(firstTwoFruits);
これは以下のように印刷します:
[apple, banana]
sortedメソッド
sorted
メソッドは、本棚を並べるようなもので、要素を並べます:
List<String> sortedFruits = fruits.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedFruits);
これは以下のように印刷します:
[apple, banana, cherry, date]
並列処理
Streamの素晴らしい機能の1つは、並列処理が簡単に行えることです。大きなデータセットでの操作を速める可能性があります。並列Streamを作成する方法は以下の通りです:
fruits.parallelStream().forEach(fruit -> System.out.println(fruit + " " + Thread.currentThread().getName()));
これは以下のように印刷される可能性があります:
banana ForkJoinPool.commonPool-worker-1
apple main
date ForkJoinPool.commonPool-worker-2
cherry ForkJoinPool.commonPool-worker-3
順序は実行するたびに異なる場合があります。なぜなら、フルーツは並列に処理されているからです!
Collectors
Collectorsは、Stream操作の結果を集める特別なツールです。私たちは例でcollect(Collectors.toList())
を使用して、Streamの結果をListに戻しています。他にも多くの便利なCollectorsがあります:
// 要素を文字列に結合
String fruitString = fruits.stream().collect(Collectors.joining(", "));
System.out.println(fruitString); // 印刷: apple, banana, cherry, date
// 要素数を数える
long fruitCount = fruits.stream().collect(Collectors.counting());
System.out.println(fruitCount); // 印刷: 4
// 要素をグループ化
Map<Character, List<String>> fruitGroups = fruits.stream()
.collect(Collectors.groupingBy(fruit -> fruit.charAt(0)));
System.out.println(fruitGroups); // 印刷: {a=[apple], b=[banana], c=[cherry], d=[date]}
統計
Streamを使用して数値データの統計を計算することもできます:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt(Integer::intValue).summaryStatistics();
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
System.out.println("Average: " + stats.getAverage());
これは以下のように印刷します:
Count: 5
Sum: 15
Min: 1
Max: 5
Average: 3.0
Java Streamsの例
すべてを合わせて、より複雑な例を見てみましょう。年齢と成績を持つ学生のリストを考えてみましょう:
class Student {
String name;
int age;
double grade;
Student(String name, int age, double grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
}
public class StreamExample {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", 22, 90.5),
new Student("Bob", 20, 85.0),
new Student("Charlie", 21, 92.3),
new Student("David", 23, 88.7)
);
// 21歳以上の学生の平均成績を取得
double averageGrade = students.stream()
.filter(student -> student.age > 21)
.mapToDouble(student -> student.grade)
.average()
.orElse(0.0);
System.out.println("21歳以上の学生の平均成績: " + averageGrade);
// 成績上位2人の学生の名前を取得
List<String> topStudents = students.stream()
.sorted((s1, s2) -> Double.compare(s2.grade, s1.grade))
.limit(2)
.map(student -> student.name)
.collect(Collectors.toList());
System.out.println("成績上位2人の学生: " + topStudents);
}
}
この例では、複数のStream操作を連結してデータを処理する方法を示しています。
そして、ここまでがJava Streamsの初歩です!練習することが大事です。これらの概念を試してみてください。間もなくプロのようにデータをストリーミングできるようになるでしょう!お楽しみに!
Credits: Image by storyset