Sparrow note リンク:
https://www.yuque.com/g/u22538081/ghlpft/zcbyis/collaborator/join?token=pofOuJabmo9rgKvS# ドキュメント「関数型プログラミング - JDK の新機能」を参照するよう招待します。
なぜ勉強するのか?
自社のコードを理解する
大量のコレクションを効率的に処理できる
コードの可読性が高い
入れ子地獄を解消する
コンセプト
オブジェクト指向の考え方は、オブジェクトが何を行うかに焦点を当てる必要があります
が、関数型プログラミングの考え方は数学における関数に似ていますが、主にデータに対してどのような操作が実行されるかに焦点を当てます。
アドバンテージ
シンプルなコード、迅速な開発
自然言語に近く、理解しやすい
簡単な「同時プログラミング」
ラムダ式
コンセプト
糖衣構文
いくつかの匿名内部クラスの記述を簡素化します。
データをどう扱うかにもっと注目してください。
芯
派生または省略可能
基本フォーマット
(パラメータリスト) —> {コード}
例一
以前は、匿名内部クラスを開始するスレッドを作成していました。
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新线程中run方法被执行了");
}
}).start();
ラムダを使用した後
/**
* 什么时候可以用lambda简化呢?
* 1、如果匿名内部类是一个接口
* 2、并且这个接口中只有一个方法需要重写(有多个就不好判断重写哪个了)
* 【关注参数和方法体】
*/
new Thread(()-> {
System.out.println("lambda表达式");
}).start();
例二
package lambda;
import java.util.function.IntBinaryOperator;
public class LambdaDemo01 {
public static void main(String[] args) {
/**
* 原来的写法
*/
int i = calculateNum(new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
// TODO Auto-generated method stub
return left + right;
}
});
System.out.println(i);
/**
* 使用lambda表达式
*/
int j = calculateNum((int left,int right) -> {
return left + right;
});
System.out.println(j);
}
//IntBinaryOperator 是函数式接口
public static int calculateNum(IntBinaryOperator operator) {
int a = 10;
int b = 20;
return operator.applyAsInt(a, b);
}
}
アイデアのショートカットキー、lambdaに変更
アイデアのショートカットキー、ラムダがオリジナルになる
例 3
package lambda;
import java.util.function.IntBinaryOperator;
import java.util.function.IntPredicate;
public class LambdaDemo01 {
public static void main(String[] args) {
/**
* 原来的写法
*/
printNum(new IntPredicate() {
@Override
public boolean test(int value) {
// TODO Auto-generated method stub
return value%2 == 0;
}
});
System.out.println("==================");
/**
* 使用lambda表达式
*/
printNum((int value) -> {
return value%2 == 0;
});
}
//IntPredicate 是一个函数式接口
public static void printNum(IntPredicate predicate) {
int[] arr = {
1,2,3,4,5,6,7,8,9,10};
for(int i :arr) {
if(predicate.test(i)) {
System.out.println(i);
}
}
}
}
例 4
package lambda;
import java.util.function.Function;
public class LambdaDemo01 {
public static void main(String[] args) {
/**
* 原来方式
*/
Integer result = typeConver(new Function<String, Integer>() {
@Override
public Integer apply(String t) {
// TODO Auto-generated method stub
return Integer.valueOf(t);
}
});
System.out.println(result);
/**
* 使用lambda表达式
*/
Integer result2 = typeConver((String t) -> {
return Integer.valueOf(t);
});
System.out.println(result2);
}
//Function 是一个函数式接口
public static <R> R typeConver(Function<String,R>function) {
String str = "1235";
R result = function.apply(str);
return result;
}
}
例 5
package lambda;
import java.util.function.Function;
import java.util.function.IntConsumer;
public class LambdaDemo01 {
public static void main(String[] args) {
/**
* 原来方式
*/
foreachArr(new IntConsumer() {
@Override
public void accept(int value) {
// TODO Auto-generated method stub
System.out.println(value);
}
});
/**
* 使用lambda表达式
*/
foreachArr((int value) -> {
System.out.println(value);
});
}
//Function 是一个函数式接口
public static void foreachArr(IntConsumer consumer) {
int[] arr = {
1,2,3,4,5,6,7,8,9,10};
for(int i : arr) {
consumer.accept(i);
}
}
}
ルールの省略
パラメータの型は省略可能です。
メソッド本体のコードが 1 行のみの場合、中括弧のリターンと唯一のコード行のセミコロンは省略できます。
メソッドのパラメータが 1 つだけの場合、括弧は省略できます
。上記のルールは覚えられないか、省略できます。
例を省略
package lambda;
import java.util.function.Function;
import java.util.function.IntConsumer;
public class LambdaDemo01 {
public static void main(String[] args) {
/**
* 使用lambda表达式
*/
foreachArr((int value) -> {
System.out.println(value);
});
/**
* 省略后
*/
foreachArr(value -> System.out.println(value));
}
//Function 是一个函数式接口
public static void foreachArr(IntConsumer consumer) {
int[] arr = {
1,2,3,4,5,6,7,8,9,10};
for(int i : arr) {
consumer.accept(i);
}
}
}
ストリーム
1。概要
Java8 の Stream は関数型プログラミング モデルを使用しており、その名前のように、コレクションや配列に対してチェーン状のストリーム操作を実行するために使用できるため、コレクションや配列に対する操作が容易になります。
2. 症例データの準備
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
著者クラス
package com.sangeng;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.List;
@Data //生成get和set
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
@EqualsAndHashCode //用于后期去重使用(重写equals和hashCode方法)
public class Author {
//作者
//id
private Long id;
//姓名
private String name;
//年龄
private Integer age;
//简介
private String intro;
//作品
private List<Book> books;
}
本
package com.sangeng;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Book {
//书
//id
private Long id;
//书名
private String name;
//分类
private String category; //哲学,小说
//评分
private Integer score;
//简介
private String intro;
}
データ
package com.sangeng;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class StreamDemo {
public static void main(String[] args) {
List<Author> authors = getAuthors();
}
private static List<Author> getAuthors(){
//数据初始化
Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
Author author2 = new Author(2L,"亚拉索",15,"狂风也追逐不上他的思考速度",null);
Author author3 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);
Author author4 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);
//书籍列表
List<Book> books1 = new ArrayList<>();
List<Book> books2 = new ArrayList<>();
List<Book> books3 = new ArrayList<>();
books1.add(new Book(1L,"刀的两侧是光明与黑暗","哲学,爱情",88,"用一把划分了爱情"));
books1.add(new Book(2L,"一个人同一把刀","个人成长,爱情",99,"讲述如何从"));
books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你去世界尽头"));
books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你去世界尽头"));
books2.add(new Book(4L,"吹或不吹","爱情,个人传记",56,"一个哲学家的爱情"));
books3.add(new Book(5L,"刀的两侧是光明与黑暗","爱情",56,"无法想象一个武者"));
books3.add(new Book(6L,"风与剑","个人传记",100,"讲述如何从"));
books3.add(new Book(6L,"风与剑","个人传记",100,"讲述如何从"));
author.setBooks(books1);
author2.setBooks(books2);
author3.setBooks(books3);
author4.setBooks(books3);
List<Author> authorsList = new ArrayList<>(Arrays.asList(author,author2,author3,author4));
return authorsList;
}
}
3. クイックスタート
要件:
getAuthors メソッドを呼び出して、著者のコレクションを取得できます。次に、年齢が 18 歳未満のすべての作家の名前を出力し、重複排除に注意する必要があります。
達成:
package com.sangeng;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class StreamDemo {
public static void main(String[] args) {
List<Author> authors = getAuthors();
//System.out.println(authors);
//打印所有年龄小于18的作家名字,并且要注意去重
//filter过滤
//forEach遍历
authors.stream() //stream()把集合转换成流
.distinct() //distinct()去重
.filter(author -> author.getAge() < 18) //filter过滤【名字小于18】
.forEach(author -> System.out.println(author.getName())); //forEach遍历
}
private static List<Author> getAuthors(){
//数据初始化
Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
Author author2 = new Author(2L,"亚拉索",15,"狂风也追逐不上他的思考速度",null);
Author author3 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);
Author author4 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);
//书籍列表
List<Book> books1 = new ArrayList<>();
List<Book> books2 = new ArrayList<>();
List<Book> books3 = new ArrayList<>();
books1.add(new Book(1L,"刀的两侧是光明与黑暗","哲学,爱情",88,"用一把划分了爱情"));
books1.add(new Book(2L,"一个人同一把刀","个人成长,爱情",99,"讲述如何从"));
books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你去世界尽头"));
books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你去世界尽头"));
books2.add(new Book(4L,"吹或不吹","爱情,个人传记",56,"一个哲学家的爱情"));
books3.add(new Book(5L,"刀的两侧是光明与黑暗","爱情",56,"无法想象一个武者"));
books3.add(new Book(6L,"风与剑","个人传记",100,"讲述如何从"));
books3.add(new Book(6L,"风与剑","个人传记",100,"讲述如何从"));
author.setBooks(books1);
author2.setBooks(books2);
author3.setBooks(books3);
author4.setBooks(books3);
List<Author> authorsList = new ArrayList<>(Arrays.asList(author,author2,author3,author4));
return authorsList;
}
}
素早いキー操作
debug Stream流
Javaコンテンツがメソッドになる
4. 日常業務
1. ストリームを作成する
単一列コレクション: コレクション object.Stream
List<Author> authors = getAuthors();
Stream<Author> stream = authors.stream();
配列: Arrays.stream (配列) または Stream.of を使用して作成します
Integer[] arr = {
1,2,3,4,5};
Stream<Integer> stream1 = Arrays.stream(arr);
Stream<Integer> stream2 = Stream.of(arr);
2 列のコレクション: 1 列のコレクションに変換して作成します。
Map<String,Integer> map = new HashMap<>();
map.put("垃圾公司",19);
map.put("黑子",17);
map.put("日向源",16);
Stream<Map.Entry<String,Integer>> stream3 = map.entrySet().stream();//entrySet()把map集合key,value封装成Set
//上一句分开
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Stream<Map.Entry<String, Integer>> stream4 = entrySet.stream();
2. 中間操作
フィルターフィルター
ストリーム内の要素は条件付きでフィルタリングでき、フィルタ条件を満たす要素のみをストリームに残すことができます。
例:
名前の長さが 1 より大きいすべてのライターの名前を出力します。
//打印所有名字长度大于1的作家的姓名
List<Author> authors = getAuthors();
authors.stream()
.filter(author -> author.getName().length() > 1) //过滤 名字长度大于1
.forEach(author -> System.out.println(author.getName()));
地図
対流内の要素を計算または変換できます。
例:
すべてのライター名を出力する
// map 打印所有作家的名字
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getName()) //获取所有名字
.forEach(name -> System.out.println(name));
//map 所有年龄加10
authors.stream()
.map(author -> author.getAge())
.map(age -> age + 10)
.forEach(age -> System.out.println(age));
明確
重複した要素は削除可能
たとえば、
すべての作成者の名前を出力し、要素が重複しないように要求します。
注:distinct メソッドは、Object の等しいメソッドに依存して、それらが同じオブジェクトであるかどうかを判断します。したがって、equals メソッドの書き換えに注意してください
//打印所有作家的名字,并且要求其中不能有重复元素
List<Author> authors = getAuthors();
authors.stream()
.distinct() //去重
.forEach(author -> System.out.println(author.getName()));
並べ替えられた
ストリーム内の要素はソート可能
例:
ストリーム内の要素を経過時間に応じて降順に並べ替え、重複する要素を必要としない
注: 空のパラメーターを指定してsorted() メソッドを呼び出す場合、ストリーム内の要素はComparable インターフェイスを実装する必要があります。
//对流中的元素按照年龄进行降序排序,并且要求不能有重复元素
List<Author> authors = getAuthors();
authors.stream()
.sorted() //排序 无参sorted()使用排序需要实现Comparable排序接口
.distinct() //去重
.forEach(author -> System.out.println(author.getName()));
authors.stream()
.distinct()
.sorted((o1,o2) -> o2.getAge() - o1.getAge()) //排序 有参sorted直接匿名内部类实现Comparable排序接口
.forEach(author -> System.out.println(author.getName()));
パラメータがソートされていないため、ストリーム内の要素はComparable インターフェイスを実装する必要があります
package com.sangeng;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.List;
@Data //生成get和set
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
@EqualsAndHashCode //用于后期去重使用(重写equals和hashCode方法)
public class Author implements Comparable<Author>{
//作者
//id
private Long id;
//姓名
private String name;
//年龄
private Integer age;
//简介
private String intro;
//作品
private List<Book> books;
@Override
public int compareTo(Author o) {
return this.getAge() - o.getAge();
}
}
限界
ストリームの最大長を設定でき、超過部分は破棄されます。
例:
ストリーム内の要素を経過時間に従って降順に並べ替え、重複する要素がないことを要求し、その名前を出力します。二人の最古の作家
//对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素,然后打印其中年龄最大的两个作家名字
List<Author> authors = getAuthors();
authors.stream()
.distinct()// 去重
.sorted((o1, o2) -> o2.getAge() - o1.getAge()) //排序,倒序
.limit(2) //设置流的最大长度,超出的部分将被抛弃
.forEach(author -> System.out.println(author.getName() +" " +author.getAge()));
スキップ
ストリーム内の最初の n 要素をスキップし、残りの要素を返します。
例:
最も古いライターを除く他のライターを出力し、重複する要素を必要とせず、経過時間の降順で並べ替えます。
//打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排序
List<Author> authors = getAuthors();
authors.stream()
.distinct() //去重
.sorted((o1, o2) -> o2.getAge() - o1.getAge()) //降序排序
.skip(1) //skip 跳过流中的前n个元素,返回剩下的元素
.forEach(author -> System.out.println(author.getName() +" " +author.getAge()));
フラットマップ
map は、ストリーム内の要素として 1 つのオブジェクトを別のオブジェクトに変換することしかできません。また、 flatMap は、ストリーム内の要素として 1 つのオブジェクトを複数のオブジェクトに変換できます。
例 1
すべての書籍の名前を出力し、重複要素の重複排除を要求します。
//map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。
//打印所有书籍的名字,要求对重复的元素进行去重。
List<Author> authors = getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream()) // 把获取到的所有一个个的书籍对象再转为流
.distinct() //去重
.forEach(book -> System.out.println(book));
System.out.println("==========");
authors.stream()
.map(author -> author.getBooks())// 使用map的话,一个作者的书籍返回一个集合,不是一个个的书籍对象。
.distinct()
.forEach(book -> System.out.println(book));
例 2
既存のデータのすべての分類を出力します。分類の重複排除が必要ですが、この形式は表示できません: 哲学、愛。
//flatMap 打印现有数据的所有分类,要求对分类进行去重,不能出现这种格式:哲学,爱情。
List<Author> authors = getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream()) //获取所有书
.distinct() //对书去重
.flatMap(book -> Arrays.stream(book.getCategory().split(","))) //获取所有书的分类
.distinct() //对数的分类去重
.forEach(category -> System.out.println(category));
3. 操作を終了する
それぞれに
ストリーム内の要素をトラバースするには、パラメータを渡して、トラバースされた要素に対してどのような特定の操作が実行されるかを指定します。
例:
すべてのライターの名前を出力します。
//forEach 输出所有作家的名字
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getName())
.distinct()
.forEach(name -> System.out.println(name));
カウント
現在のストリーム内の要素の数を取得するために使用できます。
例:
これらの著者が出版した本の数を出力します。重複する要素の削除に注意してください。
//count 打印这些作家的所出书籍的数目,注意删除重复元素
List<Author> authors = getAuthors();
long count = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct() //书信息进行去重
.map(book -> book.getName())
.distinct() //书名进行去重(由于我的测试数据,书名相同,但是有些分类写的是不同的)
.count(); //计算 所有书的个数
System.out.println(count);
//老师这个【可能是没想到书名相同,但是分类没相同,这个是测试数据没写好】
long count1 = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct() //书信息进行去重
.count();
System.out.println(count1);
最大最小
ストリーム内の最高値を取得するために使用できます。
例:
これらの著者が出版した本の最高スコアと最低スコアを取得して印刷します。
//max min 分别获取这些作家的所出书籍的最高分和最低分并打印。
List<Author> authors = getAuthors();
Optional max = authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore() )
.max((score1,score2) -> score1-score2);
System.out.println("最高评分 " + max.get());
Optional min = authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore() )
.min((score1,score2) -> score1-score2);
System.out.println("最低评分 " + min.get());
収集
現在のストリームをコレクションに変換します。
Collectors.toList()
Collectors.toSet()
Collectors.toMap() [キーと値]
例:
作成者の名前を格納する List コレクションを取得します。
//collect
//获取一个存放说有作者名字的List集合。
List<Author> authors = getAuthors();
List<String> name = authors.stream()
.map(author -> author.getName())
.distinct()
.collect(Collectors.toList());// 把流转换成List
System.out.println(name);
すべての本のタイトルのセット コレクションを取得する
// 获取一个所有书名的Set集合。
Set<String> bookName = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.map(book -> book.getName())
.collect(Collectors.toSet());
System.out.println(bookName);
マップ コレクションを取得します。マップのキーは作成者の名前、値はリストです [キーと値の 2 つの関数を作成します]
// 获取一个map集合map的key为作者名,value为List<Book> 【map的话就要写两个方法】
Map<String,List<Book>> collect = authors.stream()
.distinct()
//map 两个返回值用“,”隔开(返回key,返回value)
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
System.out.println(collect);
anyMatch、allMatch、noneMatch を検索して一致させます
- - - - - - - - マッチ - - - - - - - -
任意の一致
条件に合致する要素があるかどうかを判定する場合に使用でき、結果はブール型になります
例:
29歳以上のライターがいるかどうかを判定する場合
//anyMatch可以用来判断是否有任意符合匹配条件的元素,结果为Boolean类型
//例子:判断是否有年龄在29岁以上的作家
List<Author> authors = getAuthors();
Boolean test = authors.stream()
.anyMatch(author -> author.getAge() > 29);
System.out.println(test);
すべて一致
すべての一致条件が満たされているかどうかを判定するために使用でき、結果はブール型になります。すべてが一致する場合、結果は true、それ以外の場合、結果は false
例:
すべてのライターが成人かどうかを判断する
//allMatch可以用来判断是否都符合匹配条件,结果为Boolean类型。如果都匹配结果为true,否则为false
//例子:判断是否所有作家都成年了
Boolean test2 = authors.stream()
.allMatch(author -> author.getAge()>18);
System.out.println(test2);
なし一致
フロー内の要素が一致条件を満たしていないかを判定し、結果が満たしていない場合は結果を true、そうでない場合は false 例:筆者が 100 歳を超えていないか
を
判定する
//noneMatch可以判断流中的元素是否都不符合匹配条件,如果都不符合结果为true,否则为false
//例子:判断作家是否都没超过100岁
Boolean test3 = authors.stream()
.noneMatch(author -> author.getAge()>=100);
System.out.println(test3);
- - - - - - - - 探す - - - - - - - -
任意の検索
ストリーム内の任意の要素を取得します。このメソッドは、ストリーム内の最初の要素を取得する必要があることを保証しません。
例:
18 歳以上のライターを取得し、存在する場合はその名前を出力します。
//findAny 获取流中的任意一个元素。该方法没有保证获取的一定是流中的第一个元素。
//例子:获取任意一个大于18的作家,如果存在就输出他的名字
List<Author> authors = getAuthors();
Optional<Author> optionalAny = authors.stream()
.filter(author -> author.getAge() > 18)
.findAny();//获取流中的任意满足条件的一个元素
//有问题,年龄改成0,还是同一个人(不是说是随机的吗?怎么还是第一个,是我代码有问题,还是概率问题)
optionalAny.ifPresent(author -> System.out.println(author.getName()));
最初に見つける
ストリーム内の最初の要素を取得します。
例:
最年少の作家を取得し、その名前を出力します
//findFirst 获取流中的第一个元素。
//例子:获取一个年龄最小的作家,并输出他的姓名
List<Author> authors = getAuthors();
Optional<Author> first = authors.stream()
.sorted((o1, o2) -> o1.getAge() - o2.getAge())
.findFirst();
first.ifPresent(author -> System.out.println(author.getName()));
マージを減らす
ストリーム内のデータを入力し、
指定した計算方法に従って結果を計算(リダクション演算)します。要素と初期値が計算され、計算結果は以下の要素で計算されます。
Reduce の 2 つのパラメーターのオーバーロード形式の内部計算は次のとおりです。
T result = identity;
for(T element : this stream)
result = accumulator.apply(result,element)
return result;
Reduce のオーバーロード形式の内部計算方法は次のとおりです。最初の要素を初期値に代入してから計算します。
boolean foundAny = false;
T result = null;
for (T element : this stream) {
if (!foundAny) {
foundAny = true;
result = element;
}
else
result = accumulator.apply(result, element);
}
return foundAny ? Optional.of(result) : Optional.empty();
このうち、identity はメソッド パラメータを通じて渡すことができる初期値であり、アキュムレータの適用によってどのような計算が実行されるかもメソッド パラメータを通じて決定されます。
//reduce简单理解(模拟)
int[] arr = {
1,2,3,4};
int resut = 0;// 初始值
for (int i : arr){
resut = resut + 1;// 具体操作
}
System.out.println(resut);
例:
reduce を使用して、すべての著者の年齢の合計を確認します。
//使用reduce求所有作者的年龄和
List<Author> authors = getAuthors();
Integer sum = authors.stream()
.distinct() // 去重
.map(author -> author.getAge()) // 获取所有年龄
// (初始值,(附上初始值,要操作的元素))
.reduce(0, (result, element) -> result + element);
System.out.println(sum);
reduce を使用して著者の最大年齢を見つけます
//使用reduce求所作者中年龄的最大值
List<Author> authors = getAuthors();
Integer max = authors.stream() //其实max() 是封装好的reduce
.distinct()
.map(author -> author.getAge()) // 获取年龄
// 初始值(最小值) 附上初始值,要操作的元素 -> 三目,算出最大值
.reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
System.out.println(max);
reduce を使用して、すべての著者の中から最低年齢を見つけます
//使用reduce求所有作者中年龄的最小值
List<Author> authors = getAuthors();
Integer min = authors.stream()
.distinct()
.map(author -> author.getAge())
// 初始值(最大值) 附上初始值,要操作的元素 -> 三目,算出最小值
.reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);
/*
Optional<Integer> min = authors.stream().distinct().map(Author::getAge).reduce(Integer::min);
System.out.println(min);
*/
System.out.println(min);
すべての著者の最小年齢を見つけるには、1 つのパラメーターの形式でreduce を使用します。
// reduce一个参数的用法
//使用reduce求所有作者中年龄的最小值
List<Author> authors = getAuthors();
Optional<Integer> minOptional = authors.stream()
.map(author -> author.getAge())
//reduce一个参数,把第一个元素赋给初始值
.reduce((result, element) -> result > element ? element : result);
minOptional.ifPresent(age -> System.out.println(age));
ストリームノート
遅延評価 (ファイナライズ操作がない場合、中間操作は実行されません)
ストリームは使い捨て可能です (ストリーム オブジェクトがファイナライズ操作を受けると、そのストリームは再度使用できません)
元のデータには影響しません (ストリーム内のデータに対して多くの処理が行われますが、通常の状況では、元のコレクション内の要素には影響しません。これは多くの場合、期待どおりです)
オプション
概要:
コードを記述するときに最もよく発生するのは、NULL ポインター例外です。したがって、多くの場合、空ではないさまざまな判断を行う必要があります
。
List<Author> author = getAuthor();
if (author != null){
System.out.println(author.getName);
}
·特にオブジェクト内のプロパティがまだオブジェクトではない場合。この種の判定はさらに多くなります
。判定ステートメントが多すぎるとコードが肥大化します
。そのため、JDK8 では Optional が導入されました。Optional を使用する習慣が身につくと、null ポインタ例外を避けるためのより洗練されたコードを書くことができます。
・そして、Optionalは関数型プログラミング関連のAPIでも多く使われており、Optionalの使い方を知らないと関数型プログラミングの学習にも影響します。
任意使用
オブジェクトを作成する
Optional はラッパー クラスのようなもので、Optional 内に特定のデータをカプセル化できます。その後、Optional のカプセル化されたメソッドを使用して、カプセル化されたデータを操作して、null ポインター例外を非常にエレガントに回避できます。
1. 通常、 Optional の静的メソッド ofNullableを使用して、データを Optional オブジェクトにカプセル化します。受信パラメータが null であるかどうかに関係なく、問題はありません。
集中
Author author = getAuthor();
Optional<Author> authorOptional = Optional.ofNullable(author);
全て
package com.sangeng;
import java.util.Optional;
import java.util.function.Consumer;
public class OptionalDemo {
public static void main(String[] args) {
Author author = getAuthor();
Optional<Author> authorOptional = Optional.ofNullable(author);
// ifPresent如果存在(如果数据为null,这里是不会被消费的)
authorOptional.ifPresent(author1 -> System.out.println(author1.getName()));
}
public static Author getAuthor(){
Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
return author;// return null;
}
}
データをカプセル化するコードを追加するのは面倒だと思うかもしれませんが、戻り値がカプセル化された Optional になるように getAuthor メソッドを変更すると、はるかに使いやすくなります。(推奨用途)
package com.sangeng;
import javax.swing.text.html.Option;
import java.util.Optional;
import java.util.function.Consumer;
public class OptionalDemo {
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthorOptional();
authorOptional.ifPresent(author -> System.out.println(author.getName()));
}
public static Optional<Author> getAuthorOptional(){
Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
return Optional.ofNullable(author);//直接返回一个Optional
}
}
実際の開発では、多くのデータがデータベースから取得されます。Mybatis はバージョン 3.5 から Optional をサポートできるようになりました。dao メソッドの戻り値の型を Optional 型として直接定義でき、MyBatis はデータを Optional オブジェクトにカプセル化して返します。カプセル化プロセスには独自の操作は必要ありません。
オブジェクトが空ではないことが確実な場合は、 のOptional静的メソッドを使用して、データを Optional オブジェクトにカプセル化できます (推奨されません
)。
Author author = getAuthor();
Optional<Author> author1 = Optional.of(author);
package com.sangeng;
import javax.swing.text.html.Option;
import java.util.Optional;
import java.util.function.Consumer;
public class OptionalDemo {
public static void main(String[] args) {
Author author = getAuthor();
Optional<Author> author1 = Optional.of(author);
author1.ifPresent(author2 -> System.out.println(author2.getName()));
}
public static Author getAuthor(){
Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
return author;//return null;
}
}
of を使用する場合、渡されるパラメーターは null であってはいけないことに注意してください。(null を渡してみるとどうなりますか)
of メソッドを使用します。データが空の場合は、null ポインタ例外 java.lang.NullPointerException が報告されます。
メソッドの戻り値の型がOptional型の場合。そして、特定の計算の戻り値が null であると判断した場合は、null を Optional オブジェクトにカプセル化して返す必要があります。現時点では、オプションの静的メソッド empty を使用してカプセル化できます。
Optional.empty();
public static Optional<Author> getAuthorOptional(){
Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
return author == null?Optional.empty():Optional.of(author);
}
どちらが便利だと思いますか?
今後は Optional.ofNullable() を使用します
安全な消費
Optional オブジェクトを取得したら、必ずそのオブジェクト内のデータを使用する必要があります。このとき、値を消費するにはifPresentメソッドを使用します。このメソッドは、カプセル化されたデータが空かどうかを判断し、空でない場合にのみ特定の消費コードを実行します。これはより安全に使用できます。
たとえば、
次の記述方法は、null ポインタ例外をエレガントに回避します。
Optional<Author> author1 = Optional.of(author);
author1.ifPresent(author2 -> System.out.println(author2.getName()));
値を取得する
値を取得して自分で処理したい場合は、get メソッドを使用して値を取得できますが、お勧めしません。Optional 内のデータが空の場合は例外が発生するためです。
get メソッドを使用すると、データが空の場合、例外 java.util.NoSuchElementException: No value present が報告されます。
Optional<Author> author = getAuthorOptional();
Author author1 = author.get();
安全に値を取得する
値を安全に取得できることが期待される場合。get メソッドの使用は推奨しませんが、Optional が提供する次のメソッドを使用してください。
orElseGet は
データを取得し、データが空の場合はデフォルト値を設定します。データが空でなければ、データを取得できます。空の場合は、デフォルト値
として渡したパラメータに従ってオブジェクトが作成されます。
Optional<Author> authorOptional = getAuthorOptional();
//如果有值,就返回Author。如果没有值就返回默认值orElseGet(设置默认值)
Author author = authorOptional.orElseGet(() -> new Author());
Optional<Author> authorOptional = getAuthorOptional();
//如果有值,就返回Author。如果没有值就返回默认值orElseGet(设置默认值)
Author author = authorOptional.orElseGet(() -> new Author(2L,"大雨",33,"一个从菜刀中明悟哲理的祖安人",null));
System.out.println(author.getName());
public static Optional<Author> getAuthorOptional(){
Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
//Author author = null;
return author == null?Optional.empty():Optional.of(author);
}
orElseThrow は
データを取得します。データが空でなければ、データを取得できます。空の場合、に渡したパラメータに基づいて例外がスローされます。
教師テンプレート
Optiona1<Author> authoroptiona1 = Optional.ofNu1lable(getAuthor 0));
try {
Author author = authoroptional.orElseThrow((Supplier<Throwable>) () ->
new RuntimeException("author为空"));
System.out .printIn(author .getName());
} catch (Throwable throwable) {
throwable.printstackTrace();
};
戦闘テスト
Optional<Author> authorOptional = getAuthorOptional();
try {
//如果author,就输出author。如果为空,就抛出异常。
Author author = authorOptional.orElseThrow(() -> new RuntimeException("数据为null"));
System.out.println(author);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
public static Optional<Author> getAuthorOptional(){
Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
//Author author = null;
return author == null?Optional.empty():Optional.of(author);
}
フィルター
filter メソッドを使用してデータをフィルタリングできます。元々データがあっても判定に当てはまらない場合はデータの無いOptionalオブジェクトとなります。
Optional<Author> authorOptional = getAuthorOptional();
authorOptional.filter(author -> author.getAge() > 18)
.ifPresent(author -> System.out.println(author.getName()));
public static Optional<Author> getAuthorOptional(){
Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
//Author author = null;
return author == null?Optional.empty():Optional.of(author);
}
判断
データがあるかどうかはisPresentメソッドを使用して判断できます。空の場合は戻り値が false になり、空でない場合は戻り値が true になります。ただし、このメソッドは Optional の利点を反映していないため、ifPresent メソッドを使用することをお勧めします。
Optional<Author> authorOptional = getAuthorOptional();
if (authorOptional.isPresent()) {
System.out.println(authorOptional.get().getName());
}
データ変換
Optional はデータの変換を可能にするマップも提供しており、変換されたデータは依然として Optional によってパッケージ化されているため、安全な使用が保証されています。たとえば、著者別の書籍のコレクションを取得したいとします
。
Optional<Author> authorOptional = getAuthorOptional();
authorOptional.map(author -> author.getBooks())
.ifPresent(books -> System.out.println(books));
Optional<Author> authorOptional = getAuthorOptional();
Optional<List<Book>> optionalBooks = authorOptional.map(author -> author.getBooks());
optionalBooks.ifPresent(books -> System.out.println(books));
要約する
创建Optional:ofNullable
Optional authorOptional = Optional.ofNullable(author);
安全消费:ifPresent
authorOptional.ifPresent(author2 -> System.out.println(author2.getName()));
機能インターフェイス
概要
抽象メソッドが 1 つだけあるインターフェイスは関数インターフェイスと呼ばれ、
JDK の関数インターフェイスには **@FunctionallInterface** アノテーションが付けられます。ただし、アノテーションが追加されているかどうかに関係なく、インターフェイスに抽象メソッドが 1 つだけ存在する限り、それは関数型インターフェイスです。
共通の機能インターフェイス
Consumer Consumer 消費インターフェース
の抽象メソッドのパラメータ リストと戻り値の型に従って、メソッド内の受信パラメータを消費できます。
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> {
accept(t); after.accept(t); };
}
関数計算変換インターフェース 抽象
メソッドのパラメータリストと戻り値の型に従って、メソッド内の入力パラメータを計算または変換し、結果を返すことができます。
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
述語判定インターフェース
抽象メソッドのパラメータリストと戻り値の型に応じて、メソッド内で入力されるパラメータの条件を判定し、判定結果を返すことができます。
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ANDed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* AND of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
サプライヤー生産インターフェース
抽象メソッドのパラメータリストと戻り値の型に従って、メソッド内でオブジェクトを作成し、作成したオブジェクトを返すことができます
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
![image.png](https://img-blog.csdnimg.cn/img_convert/b8949435efdda633d4b4c8fbd258902b.png#averageHue=#f3f3dc&clientId=u3b1cbb5a-c114-4&crop=0&crop=0&crop=1&crop=1&from=paste&height =251&id=ua0206448&マージン= [オブジェクト オブジェクト]&name=image.png&originHeight=282&originWidth=1001&originalType=binary&ratio=1&rotation=0&showTitle=false&size=177121&status=done&style=none&taskId=u70a12f12-cc60-4b11-ba18-a6c487dedb0&title=&width=889。 7777777777778)
一般的なデフォルトメソッド
また、
Predicate インターフェイスを使用する場合、判断条件を接続する必要がある場合があります。and メソッドは、&& を使用して 2 つの判定条件を結合するのと同じです
。例:
年齢が 17 歳以上、名前の長さが 1 より大きい印刷作家。
//打印作家中年龄大于17并且姓名的长度大于1的作家。
List<Author> authors = getAuthors();
authors.stream()
.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() > 17;
}
}.and(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getName().length() > 1;
}
}))
.forEach(author -> System.out.println(author));
あるいは、
Predicate インターフェースを使用する場合、判定条件を接続する必要がある場合があります。or メソッドは、2 つの判定条件を結合するために使用するのと同じです (
例:
年齢が 17 歳以上、または名前の長さが 2 未満の活字作家)。
//打印作家中年龄大于17或者姓名的长度小于2的作家。
List<Author> authors = getAuthors();
authors.stream()
.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge()>17;
}
}.or(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getName().length() < 2;
}
})).forEach(author -> System.out.println(author.getName()));
Predicate インターフェイスのメソッドを否定します。negate メソッドは、否定を示すために判定加算の前に ! を追加することと同じです
。例:
年齢が 17 歳以下の印刷作家。
//打印作家中年龄不大于17的作家。
List<Author> authors = getAuthors();
authors.stream()
.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() > 17;
}
}.negate()).forEach(author -> System.out.println(author));
メソッドリファレンス
ラムダを使用する場合、メソッド本体 (構築メソッドを含む) にメソッド呼び出しが 1 つしかない場合は、メソッド参照を使用してコードをさらに簡素化できます。
推奨される方法
ラムダを使用する場合、メソッド参照をいつ使用するか、どのメソッド参照を使用するか、メソッド参照の形式は何かなどを考慮する必要はありません。ラムダメソッドを書いた後、メソッド本体のコードが1行しかなく、それがメソッド呼び出しであることがわかった場合に、ショートカットキーを使ってメソッド参照に変換できるか試すだけで済みます。
より多くのメソッド参照を使用すると、ゆっくりとメソッド参照を直接記述することができます。
基本フォーマット
クラスまたはオブジェクト名::メソッド名
詳細な文法
静的メソッドの参照
実際には、それは参照クラスの静的メソッド
形式です
类名::方法名
前提条件
メソッドを書き換える場合、メソッド本体には1 行のコードのみがあり、このコード行は特定のクラスの静的メソッドを呼び出し、書き換えられる抽象メソッドのすべてのパラメーターを順番に渡します。この静的メソッド、この時点でクラスの静的メソッドを参照できます。
例:
次のコードはメソッド参照を使用して簡略化できます。
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getAge())
.map(age -> String.valueOf(age));
書き換えたメソッドにパラメーターがない場合、呼び出されたメソッドにもパラメーターがないことに注意してください。これは、上記のルールに従うことと同じです。
最適化後は次のようになります。
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getAge())
.map(String::valueOf);
参照オブジェクトのインスタンスメソッド
フォーマット
对象名::方法名
前提条件
メソッドを書き換える場合、メソッド本体には1 行のコードのみがあり、このコード行はオブジェクトのメンバー メソッドを呼び出し、書き換えられる抽象メソッドのすべてのパラメーターを順番に渡します。 member メソッドでは、この時点でオブジェクトのインスタンス メソッドを参照できます
。例:
List<Author> authors = getAuthors();
Stream<Author> authorstream = authors .stream();
StringBuilder sb = new StringBuilder();
authorstream.map(author -> author.getName())
.forEach(name->sb.append(name));
最適化:
List<Author> authors = getAuthors();
Stream<Author> authorstream = authors .stream();
StringBuilder sb = new StringBuilder();
authorstream.map(author -> author.getName())
.forEach(sb::append);
参照クラスのインスタンスメソッド
フォーマット
类名::方法名
メソッドを書き換える場合、メソッド本体には 1 行のコードのみがあり、このコード行は最初のパラメーターのメンバー メソッドを呼び出し、残りのパラメーターはすべて書き換えられる抽象メソッドに入れます。これらはすべて渡されます。このメンバメソッドに順番に取り込み、この時点でクラスのインスタンスメソッドを参照できるようになります。
例えば:
package com.sangeng;
import java.util.List;
public class MethodDemo {
interface UseString{
String use(String str,int start,int length) ;
}
public static String subAuthorName(String str, UseString useString) {
int start = 0;
int length = 1;
return useString.use(str, start, length);
}
public static void main(String[] args) {
subAuthorName("三更草堂",new UseString() {
@Override
public String use(String str, int start, int length) {
return str.substring(start,length);
}
});
}
}
最適化:
package com.sangeng;
import java.util.List;
public class MethodDemo {
interface UseString{
String use(String str,int start,int length) ;
}
public static String subAuthorName(String str, UseString useString) {
int start = 0;
int length = 1;
return useString.use(str, start, length);
}
public static void main(String[] args) {
subAuthorName("三更草堂", String::substring);
}
}
コンストラクターリファレンス
コンストラクター参照は、メソッド本体内の 1 行のコードがコンストラクターである場合に使用できます。
フォーマット
类名::new
メソッドを書き換える場合、メソッド本体には 1 行のコードのみがあり、このコード行は特定のクラスのコンストラクターを呼び出し、書き換えられる抽象メソッドのすべてのパラメーターをこの構築メソッドに順番に渡します。、この時点でコンストラクターを参照できます。
例えば:
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getName())
.map(name -> new StringBuilder(name))
.map(sb -> sb.append("-三更").toString())
.forEach(str -> System.out.println(str));
最適化:
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getName())
.map(StringBuilder::new)
.map(sb -> sb.append("-三更").toString())
.forEach(str -> System.out.println(str));
高度な使用法
基本的なデータ型の最適化
以前に使用していた Stream メソッドの多くはジェネリックスを使用していました。したがって、関連するパラメーターと戻り値はすべて参照データ型です。
整数と小数を操作する場合でも、実際にはそれらのラッパー クラスを使用します。JDK5 で導入された自動ボックス化と自動アンボックス化により、対応するパッケージ化クラスを使用するときに基本的なデータ型を修正して使用することが容易になります。ただし、ボックス化とボックス化解除には確実に時間がかかることを知っておく必要があります。今回は消費量が非常に少ないですが。しかし、大量のデータがボックス化とボックス化解除を繰り返す場合、この時間のロスを無視することはできません。
したがって、時間消費のこの部分を最適化できるようにするためです。Stream は、プリミティブ データ型に固有のメソッドも多数提供します。
例: madTolnt、mapToLong、mapToDouble、 flatMapTolnt、 flatMapToDouble など。
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getAge())
.map(age -> age +10)
.filter(age -> age>18)
.map(age -> age+2)
.forEach(System.out::println);
//优化,(自动装箱和拆箱是要消耗时间的)
authors.stream()
.mapToInt(author -> author.getAge())// 拆箱一次,Integer->int
.map(age -> age +10) // 后面的都是int了,不用自动拆箱了。
.filter(age -> age>18)
.map(age -> age+2)
.forEach(System.out::println);
パラレルストリーム
ストリーム内に多数の要素がある場合、並列ストリームを使用して操作の効率を向上させることができます。実際、並列フローとは、タスクを複数のスレッドに割り当てて完了させることです。コードを使用して自分で実装する場合、実際には非常に複雑になるため、並行プログラミングについての十分な理解と知識が必要です。ストリームを使用する場合は、並列ストリームを使用するようにメソッドの呼び出しを変更するだけで実現できるため、効率が向上します。
パラレル方式では、シリアルストリームをパラレルストリームに変換できます。
Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9,10);
/*
Integer integer = stream.filter(num -> num > 5)
.reduce((result, element) -> result + element)
.get();
System.out.println(integer);
*/
//变成并行流(多线程完成操作)
Integer integer2 = stream.parallel() // parallel并行
.peek(new Consumer<Integer>() {
// peek调试用的
@Override
public void accept(Integer num) {
System.out.println(num +Thread.currentThread().getName());//查看是否多线程执行
}
})
.filter(num -> num > 5)
.reduce((result, element) -> result + element)
.get();
System.out.println(integer2);
また、ParallelStream メソッドを使用して、並列ストリーム オブジェクトを直接取得することもできます。
List<Author> authors = getAuthors();
authors.parallelStream() //直接变成并行流