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 收集流的元素