day13_面向對象(Stream流)
一.Lambda表達式
1.函數式編程的思想
強調/注重三個方面:輸入量,計算過程,輸出量
不注重格式/形式
2.冗餘的Runnable代碼
public class RunnableDemo {
public static void main(String[] args) {
//1.創建一個線程,並啓動,使用實現類
// new Thread(new MyRunnable()).start();
//2.創建一個線程,並啓動,使用匿名內部類
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("線程執行了...");
}
}).start();
}
}
//a.由於新建一個實現類麻煩,我們不得不採用匿名內部類
//b.由於Thread構造中指明瞭需要Runnable的實現類,我們不得不new一個Runnable
//c.由於接口中抽象方法爲run,那麼重寫葉必須是run
//d.而實際上我們真正想要的其實簡單,就是任務
3.函數式編程Lambda的體驗
new Thread(()->{
System.out.println(
Thread.currentThread().getName()+"..線程執行了..");
}).start();
4.Lambda標準格式介紹****************
(參數列表) -> {方法體;return 返回值;} Collections和Arrays裏的sort方法區別在於Arrays.sort()只能對引用類型數組進行排序
5.Lambda的參數和返回值**************
public class ComparatorDemo {
public static void main(String[] args) {
//1.集合
ArrayList<Integer> arr = new ArrayList<Integer>();
arr.add(3);
arr.add(23);
arr.add(13);
arr.add(43);
arr.add(53);
arr.add(23);
//2.排序 降序
// Collections.sort(arr, new Comparator<Integer>() {
// @Override
// public int compare(Integer o1, Integer o2) {
// //降序
// return o2 - o1;
// }
// });
//使用Lambda代替匿名內部類
Collections.sort(arr,(Integer o1, Integer o2)->{return o2 - o1;});
//3.打印
System.out.println(arr);
}
}
public class ComparatorDemo02 {
public static void main(String[] args) {
//自定義類型的數組
Dog[] dogs = new Dog[4];
//賦值
dogs[0] = new Dog(22, "旺財");
dogs[1] = new Dog(2222, "來福");
dogs[2] = new Dog(222, "哮天犬");
dogs[3] = new Dog(2, "楊戩");
//排序,按照Dog的年齡降序
// Arrays.sort(dogs, new Comparator<Dog>() {
// @Override
// public int compare(Dog o1, Dog o2) {
// //按照狗的年齡降序
// return o2.age-o1.age;
// }
// });
//使用Lambda優代替匿名內部類
Arrays.sort(dogs,(Dog o1, Dog o2)->{return o2.age-o1.age;});
//打印
for (Dog dog : dogs) {
System.out.println(dog);
}
}
}
表面上Lambda是匿名內部類的語法糖(格式簡化),但是彼此底層實現不同,匿名內部類面向對象,Lambda是面向虛擬機的,直接翻譯成虛擬機指令,執行效率更高
6.Lambda的省略格式************
a.參數的數據類型。可以無條件省略
b.如果參數只有一個,那麼小括號也可以省略
c.如果方法體和返回值語句可以寫成一句話,那麼{},return,以及;可以同時(都要省略,不然報錯)省略
//體驗一下函數式編程(Lambda)的優雅代碼
new Thread(()->{System.out.println(Thread.currentThread().getName());}).start();
//Lambda的省略格式
new Thread(()->System.out.println(Thread.currentThread().getName())).start();
//使用Lambda代替匿名內部類
Collections.sort(arr,(Integer o1, Integer o2)->{return o2 - o1;});
//Lambda的省略格式
Collections.sort(arr,(o1,o2)->o2 - o1);
//使用Lambda優代替匿名內部類
Arrays.sort(dogs, (Dog o1, Dog o2) -> { return o2.age - o1.age; });
//Lambda的省略格式
Arrays.sort(dogs, (o1, o2) -> o2.age - o1.age);
7.強烈注意:Lambda的使用前提
a.Lambda只能用於替代只有一個抽象方法的接口的匿名內部類
b.Lambda的省略格式只有以上三種,其他方面均不能省略(因爲可推導纔可省略)
二.函數式接口
1.什麼叫函數式接口
首先也是一個接口。可以支持函數式編程的接口,可以給Lambda使用的接口,就是隻有一個抽象方法的接口
//這就是函數式接口
public interface MyInterface {
void show();
}
2.函數式接口註解
@FunctionalInterface(當之後接口不符合函數式接口規範就會報錯)(Runnable和Comparator均是函數式接口)
@FunctionalInterface
public interface MyInterface {
void show();
}
3.常用的函數式接口
a.自定義函數式接口
/**
* 廚師接口
*/
@FunctionalInterface
public interface Cook {
void cookFood(String name);
}
public class CookDemo {
public static void main(String[] args) {
//調用方法
//使用具體的實現類
//method(new Cook的實現類對象());
//使用匿名內部類
// method(new Cook() {
// @Override
// public void cookFood(String name) {
// System.out.println("清蒸"+name+"做好了...");
// }
// });
//使用Lambda
method((String name)->{System.out.println("清蒸"+name+"做好了...");});
//省略
method(name->System.out.println("清蒸"+name+"做好了..."));
}
//定義方法:請一個廚師做飯
public static void method(Cook cc) {
cc.cookFood("蝦");
}
}
b.Consumer<T>函數式接口,消費接口
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);//該方法接收一個數據,沒有返回值
}
public class ConsumerTestDemo {
public static void main(String[] args) {
//調用method
// method(new 接口實現類對象());
//使用匿名內部類
method(new Consumer<String>() {
@Override
public void accept(String s) {
//打印到控制檯
System.out.println(s);
//保存到數據庫
//發送到網絡
}
});
//使用Lambda
method((String s) -> { System.out.println(s); });
//省略
method(s -> System.out.println(s));
}
//定義一個方法:接收一個消費者接口
public static void method(Consumer<String> con) {
//調用方法
con.accept("java");
}
}
c.Predicate<T>函數式接口,判斷接口
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);//判斷一個數據是否符合要求
}
public class PredicateTestDemo {
public static void main(String[] args) {
//調用方法
// method(new Predicate接口的實現類());
//使用匿名內部類
method(new Predicate<String>() {
@Override
public boolean test(String s) {
//如果字符串長度大於5個,符合
return s.length() > 5;
}
});
//使用Lambda
method((String s)->{return s.length() > 5;});
//省略
method(s->s.length() > 5);
}
//定義方法,接收一個判斷接口
public static void method(Predicate<String> pp) {
//調用
boolean b = pp.test("Hello");
System.out.println("該字符串符合要求嗎?" + b);
}
}
三.Stream流******************
1.引入:傳統的集合操作
public class ForeachDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("張無忌");
list.add("周芷若");
list.add("趙敏");
list.add("張強");
list.add("張三丰");
//1. 首先篩選所有姓張的人;
ArrayList<String> zhangs = new ArrayList<String>();
for (String name : list) {
//判斷
if (name.startsWith("張")) {
zhangs.add(name);
}
}
//2. 然後篩選名字有三個字的人;
ArrayList<String> threes = new ArrayList<String>();
for (String zhang : zhangs) {
if (zhang.length() == 3) {
threes.add(zhang);
}
}
//3. 最後進行對結果進行打印輸出
for (String name : threes) {
System.out.println(name);
}
}
}
2.循環遍歷的弊端分析
a.每次都要循環遍歷,很麻煩
b.循環是格式固定,注重形式
3.Stream的優雅寫法****************
//體驗Stream的優雅代碼
list.stream().filter(s->s.startsWith("張")).filter(s->s.length()==3).forEach(s-> System.out.println(s));
4.流式思想的概述
5.兩種獲取流的方式***************
a.集合獲取流(單列集合)
Collection集合調用stream()方法直接獲取流
b.數組獲取流
調用Stream的靜態方法of(可變參數/數組)獲取流
注意:如果是數組,該數組必須是引用類型,如果需要基本類型,寫包裝類(不然報錯)
public class StreamDemo {
public static void main(String[] args) {
// a.集合獲取流
ArrayList<String> arr1 = new ArrayList<String>();
Stream<String> orignal1 = arr1.stream();
HashSet<Integer> set = new HashSet<Integer>();
Stream<Integer> orignal2 = set.stream();
// b.數組獲取流
// Integer[] nums = {1,2,3,4,5};
// Stream<Integer> orignal3 = Stream.of(nums);
Stream<Integer> orignal3 = Stream.of(1, 2, 3, 4, 5);
}
}
6.Stream流中的常用方法
a.過濾:filter(new Predicate<T>(){});
b.統計個數:count();
c.取前幾個:limit(long count);
d.跳過前幾個:skip(long count);
e.逐個處理(消費數據):foreach(new Consumer<T>(){});
f.靜態方法合併Stream
public class StreamDemo02 {
public static void main(String[] args) {
//1.獲取流
Stream<String> orignal = Stream.of("jack", "rose", "tom", "marry", "jerry", "hanmeimei");
//2.過濾
// Stream<String> stream01 = orignal.filter(new Predicate<String>() {
// @Override
// public boolean test(String s) {
// //要字符串長度大於4
// return s.length() > 4;
// }
// });
Stream<String> stream01 = orignal.filter(s-> s.length() > 4);
//3.計數方法
long count = stream01.count();
System.out.println(count);
//4.取前幾個
Stream<String> stream02 = stream01.limit(2L);
System.out.println(stream02.count());
//5.跳過前幾個
Stream<String> stream03 = stream01.skip(2);
System.out.println(stream03.count());
//6.逐一消費
// stream01.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
stream01.forEach(s->System.out.println(s));
//創建兩個流
Stream<Integer> stream1 = Stream.of(1,2,3,4,5);
Stream<Integer> stream2 = Stream.of(1,2,3,6,7,8,9);
Stream<Integer> stream3 = Stream.of(10,20,30);
//合併
Stream<Integer> stream = Stream.concat(stream1, stream2);
Stream<Integer> stream4 = Stream.concat(stream, stream3);
//逐一消費
stream4.forEach(s-> System.out.println(s));
}
}
7.練習:集合元素的處理(傳統方式)
8.練習:集合元素的處理(Stream方式)****************
public class StreamDemo03 {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
one.add("迪麗熱巴");
one.add("宋遠橋");
one.add("蘇星河");
one.add("老子");
one.add("莊子");
one.add("孫子");
one.add("洪七公");
List<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("張無忌");
two.add("張三丰");
two.add("趙麗穎");
two.add("張二狗");
two.add("張天愛");
two.add("張三");
// 1. 第一個隊伍只要名字爲3個字的成員姓名;
// 2. 第一個隊伍篩選之後只要前3個人;
Stream<String> stream01 = one.stream().filter(s -> s.length() == 3).limit(3L);
// 3. 第二個隊伍只要姓張的成員姓名;
// 4. 第二個隊伍篩選之後不要前2個人;
Stream<String> stream02 = two.stream().filter(s -> s.startsWith("張")).skip(2L);
// 5. 將兩個隊伍合併爲一個隊伍;
Stream<String> totalStream = Stream.concat(stream01, stream02);
// 6. 打印整個隊伍的姓名信息。
totalStream.forEach(s -> System.out.println(s));
}
}
9.總結:函數拼接和終結方法
函數拼接方法:調用方法之後,返回還是流對象
filter,limit,skip,concat
所有的函數拼接方法,都支持鏈式編程
終結方法:調用方法之後,沒有返回值,或者返回不是流對象
count,foreach
所有的終結使用完之後,流已經操作完了,不能再繼續使用
總結:
-[] 能夠理解函數式編程相對於面向對象的優點
Lambda -> 動態指令編程dynamic invoke
-[] 能夠掌握Lambda表達式的標準格式
(數據類型 參數名,數據類型 參數名) -> {方法體;return 返回值;}
-[] 能夠掌握Lambda表達式的省略格式與規則
a.數據類型無條件省略
b.參數只有一個小時,小括號纔可以省略
c.{}中只有一句代碼時,{}和;和return可以同時省略
-[] 能夠使用Consumer<T>函數式接口
public abstract void accept(T t); 消費
-[] 能夠使用Predicate<T>函數式接口
public abstract void test(T t); 判斷
-[] 能夠理解流與集合相比的優點
-[] 能夠掌握常用的流操作
a.獲取流
集合:stream();
Stream.of(數組/可變參數);
b.流的操作
filter 過濾操作
count 統計操作
limit 取前幾個
skip 跳過前幾個
foreach 逐一消費
concat 合併兩個流
超綱:
流的map方法
::方法引用
collect 收集流的元素