序文
JavaのStreamストリームとは何ですか?
これは、Java でコレクションを処理する新しい方法です。これにより、コレクション内の要素を処理し、変換またはフィルターして結果を返すことができ、
Stream を使用してコレクションを簡単に処理できます。
在使用stream流的同时,很经常的会和lambda表达式配合使用
ストリームフローの 3 ステップの操作
私たちが最初に stream に触れたとき、必ずその使い方を知る必要があります。それは大きく 3 つのステップに分けることができます。
ストリームの作成
ストリームは、コレクションの stream() メソッドまたは Arrays.stream() メソッドを使用して作成できます。
中間操作
ストリームに対して中間操作を実行して、フィルタリング、変換、またはその他の操作を実行できます。一般的な中間操作には、filter()、map()、 flatMap()、distinct()、sorted()、peek() などが含まれます。これについては後で詳しく説明します。
端末操作
端末操作を実行すると、ストリームの処理がトリガーされ、結果が返されるか、副作用が発生します。一般的な端末操作には、forEach()、count()、reduce()、collect()、anyMatch()、allMatch()、noneMatch() などが含まれます。これについては後で詳しく説明します。
一般的な中間操作
filter(Predicate predicate)
フィルター
この中間操作は、ストリーム内の要素をフィルタリングして、適格な要素のみを残し、適格な要素で構成される新しいストリームを返します。
map(Function Mapper)
変換
この中間操作は、ストリーム内の要素に対して変換操作を実行し、各要素に対して対応する処理を実行して、新しいストリームを返します。
flatMap(Function mapper)
ストリームに変換
この中間操作は、ストリーム内の各要素をストリームに変換し、変換されたストリームを新しいストリームにマージして返します。
distinct()
重複排除
この中間操作は、ストリーム内の重複要素を削除し、重複要素のない新しいストリームを返します。
sorted()
分類する
この中間操作は、ストリーム内の要素を並べ替えて、並べ替えられた新しいストリームを返します。
peek(Consumer action)
消費動作
この中間操作は、ストリーム内の各要素に対して消費操作を実行し、元のストリームを返します。
limit(long maxSize)
データを傍受する
この中間操作は、ストリーム内の最初の maxSize 要素をインターセプトし、新しいストリームを返します。
skip(long n)
スキップ要素
この中間操作は、ストリーム内の最初の n 要素をスキップし、新しいストリームを返します。
需要注意的是,这些Stream中间操作可以任意串联,形成一条Stream流水线,每一个中间操作都会返回一个新的Stream,而不会改变原Stream中的元素。在使用Stream时,需要注意Stream流的特殊性质:它们只能被“消费”一次。如果需要对Stream中的数据进行多次操作,需要重新生成一个新的Stream对象。
共通の端末操作
forEach()
反復操作
この終了操作は、ストリーム内の各要素を反復処理し、指定された操作を呼び出して各要素を処理します。
count()
返品数量
端末操作では、並列 Stream を、単一のスレッドで処理できる通常の Stream に変換します。
reduce()
蓄積する
この終端操作は、指定された操作を通じて Stream 内の要素を蓄積し、計算結果を返します。
collect()
コレクションまたは配列をターンします
端末操作では、Stream 内の要素をコレクションまたは値に変換し、コレクションまたは値を返します。
max()
最大の要素を取得します
この終了操作は、ストリーム内の最大の要素を取得します。
min()
最小の要素を取得します
この終了操作は、Stream 内の最小の要素を取得します。
allMatch()
条件を満たすかどうか
この終了操作は、ストリーム内のすべての要素が指定された条件を満たすかどうかをチェックします。
anyMatch()
どちらかの条件を満たしている
この終了操作では、ストリーム内の要素が指定された条件を満たすかどうかを確認します。
noneMatch()
条件を満たすものはありません
この終了操作は、ストリーム内のどの要素も指定された条件を満たしていないかどうかをチェックします。
特別な端末操作: 上記の端末操作に加えて、次の特別な端末操作があります: findFirst()
、findAny()
、toArray()
中間操作例
ここでは中間操作の使用方法を紹介し、例を示します。
フィルター(述語述語)
例:
// 转换集合
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Ella");
// 过滤拿取长度大于4的数据
List<String> longNames = names.stream()
.filter(name -> name.length() > 4)
.collect(Collectors.toList());
// 打印结果
System.out.println(longNames);
結果:
[Alice, Charlie, David]
map(関数マッパー)
例:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
//将 Stream 中原有的整数元素转换为它们的平方数,最终将转换后的元素收集到一个新的 List 中。
List<Integer> squares = numbers.stream()
.map(num -> num * num)
.collect(Collectors.toList());
打印结果
System.out.println(squares);
結果:
[1, 4, 9, 16, 25, 36, 49, 64, 81]
flatMap(関数マッパー)
例:
// 转换嵌套了多个 List 的 List
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("apple", "banana", "orange"),
Arrays.asList("cat", "dog", "bird"),
Arrays.asList("Java", "Kotlin", "Python")
);
// 使用 flatMap() 方法将嵌套了多个 List 的 List 转换为一个扁平化的 Stream,
//然后使用 collect() 方法将转换后的所有元素收集到一个新的 List 中。
List<String> flatList = nestedList.stream()
.flatMap(list -> list.stream())
.collect(Collectors.toList());
// 打印结果
System.out.println(flatList);
結果:
[apple, banana, orange, cat, dog, bird, Java, Kotlin, Python]
明確()
例:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 2, 4, 5, 5, 6, 3, 7, 8, 9, 9);
// 使用 distinct() 方法将 Stream 中的重复元素去除,然后使用 collect() 方法将所有的不同元素收集到一个新的 List 中
List<Integer> uniqueNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 打印结果
System.out.println(uniqueNumbers);
結果:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
ソート済み()
例:
// 转换集合
List<String> names = Arrays.asList("Alice", "bob", "Charlie", "david", "Ella");
// 按照字母顺序排序
List<String> sortedNames = names.stream()
.sorted()
.collect(Collectors.toList());
// 打印结果
System.out.println(sortedNames);
結果:
[Alice, Charlie, Ella, bob, david]
若想按照自定义规则进行排序,可以在 sorted() 方法中传入一个自定义的 Comparator。以下为带Comparator的例子
例:
// 转换集合
List<String> names = Arrays.asList("Alice", "bob", "Charlie", "david", "Ella");
// 定义自定义Comparator
Comparator<String> lengthComparator = (str1, str2) -> Integer.compare(str1.length(), str2.length());
// 按照字母长度排序
List<String> sortedNames = names.stream()
.sorted(lengthComparator)
.collect(Collectors.toList());
// 打印结果
System.out.println(sortedNames);
結果:
[bob, Ella, Alice, david, Charlie]
ピーク()
例:
// 转换集合
List<String> names = Arrays.asList("Alice", "bob", "Charlie", "david", "Ella");
// 使用 peek() 方法在 Stream 中打印每个元素的原始名称和名称转换为大写后的名称。
//在执行完 peek() 方法后,Stream 仍然保持不变,我们可以继续对 Stream 进行其他操作,
//例如使用 map() 方法将名称转换为大写字母,并将所有元素收集到一个新的 List 对象中
List<String> resultNames = names.stream()
.peek(name -> System.out.println("原始值: " + name))
.map(name -> name.toUpperCase())
.peek(name -> System.out.println("处理值: " + name))
.collect(Collectors.toList());
ps: 该stream执行完,names 和resultNames 的值是一样的
結果:
原始值: Alice
处理值: ALICE
原始值: bob
处理值: BOB
原始值: Charlie
处理值: CHARLIE
原始值: david
处理值: DAVID
原始值: Ella
处理值: ELLA
制限(長い最大サイズ)
例:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 获取前三条数据
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
// 打印结果
System.out.println(limitedNumbers);
結果:
[1, 2, 3]
スキップ(長いn)
例:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 跳过前2个元素
List<Integer> resultNumbers = numbers.stream()
.skip(2)
.collect(Collectors.toList());
// 打印结果
System.out.println(resultNumbers);
結果:
[3, 4, 5]
端末操作例
ここでは、ターミナル操作の使用方法を紹介し、例を示します。
使用该操作处理完数据之后stream无法再使用
forEach()
例:
// 转换集合
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Ella");
// 打印大写字符
names.stream()
.forEach(name -> System.out.println("Hello, " + name.toUpperCase() + "!"));
結果:
Hello, ALICE!
Hello, BOB!
Hello, CHARLIE!
Hello, DAVID!
Hello, ELLA!
カウント()
例:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 计算长度
long count = numbers.stream()
.count();
// 打印结果
System.out.println("长度为: " + count);
結果:
长度为:5
減らす()
例:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 求和,需要两个参数,初始值和 BinaryOperator 对象
// 本实例用的sum()方法
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println("求和: " + sum);
結果:
求和:15
最大()
例:
// 转换集合
List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);
// 求最大值
Optional<Integer> max = numbers.stream()
.max(Integer::compareTo);
// 判空
if (max.isPresent()) {
System.out.println("最大值: " + max.get());
} else {
System.out.println("空值");
}
結果:
最大值:50
分()
例:
// 转换集合
List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);
// 求最大值
Optional<Integer> min= numbers.stream()
.min(Integer::compareTo);
// 判空
if (max.isPresent()) {
System.out.println("最小值: " + min.get());
} else {
System.out.println("空值");
}
結果:
最小值:10
allMatch()
例:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 判断所有元素是否都大于0
boolean allGreaterThanZero = numbers.stream()
.allMatch(n -> n > 0);
System.out.println("所有元素是否都大于0: " + allGreaterThanZero);
結果:
所有元素是否都大于0:true
anyMatch()
例:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 判断是否有任一元素为偶数
boolean hasEvenNumber = numbers.stream()
.anyMatch(n -> n % 2 == 0);
// 打印结果
System.out.println("是否有任一元素为偶数: " + hasEvenNumber);
結果:
是否有任一元素为偶数:true
noneMatch()
例:
// 转换集合
List<String> fruits = Arrays.asList("apple", "banana", "pear", "orange");
// 判断元素都不包含字母a
boolean noneContainsA = fruits.stream()
.noneMatch(fruit -> fruit.contains("a"));
System.out.println("元素都不包含字母a: " + noneContainsA);
結果:
元素都不包含字母a:false
収集()
ターミナルの操作は、処理されたデータをコレクションに変換することです。以下にさまざまなコレクション処理の例を示します。
リストの例に移ります:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 转List
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 打印结果
System.out.println(evenNumbers);
結果:
[2, 4]
転送セットの例:
// 转换集合
List<String> fruits = Arrays.asList("apple", "banana", "pear", "banana", "orange");
// 转Set
Set<String> fruitSet = fruits.stream()
.collect(Collectors.toSet());
// 打印结果
System.out.println(fruitSet);
結果:
[banana, apple, pear, orange]
地図に切り替える例:
// 转换集合
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35),
new Person("David", 40)
);
// 转Map
Map<String, Integer> nameToAgeMap = persons.stream()
.collect(Collectors.toMap(Person::getName, Person::getAge));
System.out.println(nameToAgeMap);
結果:
{
Alice=25, Bob=30, Charlie=35, David=40}
findFirst()
例:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 查找第一个偶数
Optional<Integer> firstEvenNumber = numbers.stream()
.filter(n -> n % 2 == 0)
.findFirst();
// 打印结果
if (firstEvenNumber.isPresent()) {
System.out.println("第一个偶数为: " + firstEvenNumber.get());
} else {
System.out.println("空值");
}
結果:
第一个偶数为:2
findAny()
例:
// 转换集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 查找任一一个偶数
Optional<Integer> anyEvenNumber = numbers.stream()
.filter(n -> n % 2 == 0)
.findAny();
// 打印结果
if (anyEvenNumber.isPresent()) {
System.out.println("任一偶数: " + anyEvenNumber.get());
} else {
System.out.println("空值");
}
結果:
任一偶数:2
toArray()
例:
// 转换集合
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 转Arrary
String[] namesArray = names.stream()
.toArray(String[]::new);
// 打印结果
System.out.println(Arrays.toString(namesArray));
結果:
[Alice, Bob, Charlie]
共通ストリーム処理
次の例は、私の作業プロセスでよく使用されるストリーム処理の例の一部です。参考として使用でき、随時更新されます。
List< Object > フィールドの 1 つをリストとして取得し、重複を削除します
例:
// 转换集合
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35),
new Person("David", 40)
);
// 获取名称
List<String> names = persons.stream()
.map(Person::getName)
.distinct()
.collect(Collectors.toList());
// 打印结果
System.out.println(names);
結果:
[Alice, Bob, Charlie, David]
いずれかのフィールドでグループ化された List<Object>
例:
// 转换集合
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35),
new Person("David", 40),
new Person("Alice", 45)
);
// 根据名称分组
Map<String, List<Person>> personsByName = persons.stream()
.collect(Collectors.groupingBy(Person::getName));
// 打印结果
System.out.println(personsByName);
結果:
{
Alice=[Person{
name='Alice', age=25}, Person{
name='Alice', age=45}],
Bob=[Person{
name='Bob', age=30}],
Charlie=[Person{
name='Charlie', age=35}],
David=[Person{
name='David', age=40}]
}
いずれかのフィールドに従ってソートされた List<Object>
例:
// 转换集合
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35),
new Person("David", 40),
new Person("Alice", 45)
);
// 根据年龄排序
List<Person> sortedPersons = persons.stream()
.sorted(Comparator.comparing(Person::getAge))
.collect(Collectors.toList());
// 打印结果
System.out.println(sortedPersons);
結果:
[Person{
name='Alice', age=25}, Person{
name='Bob', age=30}, Person{
name='Charlie', age=35}, Person{
name='David', age=40}, Person{
name='Alice', age=45}]
List と List は、2 つのコレクション内のまったく同じフィールド属性を持つデータを取得します
将两个 List 转换为 Stream,使用 flatMap 将两个 Stream 合并。
使用 filter 方法过滤其中一个字段属性一模一样的数据。可以使用 equals 方法进行比较,或者使用自定义的比较器。
如果存在匹配的数据,使用 findFirst 方法获取第一个匹配的元素。
例:
Optional<Object1> result = list1.stream()
.flatMap(o1 -> list2.stream()
.filter(o2 -> o2.getField().equals(o1.getField()))
.map(o2 -> o1))
.findFirst();
このうち、getField()はオブジェクト内のプロパティを取得するメソッドです。Object1 と Object2 は
それぞれ 2 つのクラスの型を表しており、独自に定義したクラス名とプロパティ名に置き換える必要があります。一致しない場合があるため、戻り値にはOptional型を使用してください。
エピローグ
以上がJAVA 8でのストリームの使い方です。