一.Lambda表达式
1.Lambda表达式
注意: 函数式接口:接口中只有一个抽象方法。
(参数1,参数2): 抽象方法的参数
->: 分隔符
{}:表示抽象方法的实现
(1) lambda基本用法
package com.wt.practice.lx01;
/**
* @Author wt
* @Date 2022/7/19 19:00
* @PackageName:com.wt.practice.lx01
* @ClassName: Demo01
* @Description: TODO
* @Version 1.0
*/
public class Demo01 {
public static void main(String[] args) {
//引入自定义接口类
My my = new My();
Thread thread = new Thread(my);
thread.start();
//创建匿名对象
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("这是匿名内部类的任务对象");
}
};
Thread thread1 = new Thread(runnable);
thread1.start();
//lambda表达式
Runnable runnable1 = ()-> System.out.println("这是lambda类型的任务对象");
Thread thread2 = new Thread(runnable1);
thread2.start();
}
}
class My implements Runnable{
@Override
public void run() {
System.out.println("这是自定义任务型接口");
}
}
Thread 类需要 Runnable 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心
为了指定 run 的方法体,不得不需要 Runnable 接口的实现类
为了省去定义一个 Runnable 实现类的麻烦,不得不使用匿名内部类
必须覆盖重写抽象 run 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错
而实际上,似乎只有方法体才是关键所在。
这时可以使用lambda表示完成上面的要求
//lambda表达式 //Runnable task03 = ()-> { // System.out.println("这是使用Lambda表达式完成的"); //}; 简写(省略了{}) Runnable runnable1 = ()-> System.out.println("这是lambda类型的任务对象"); Thread thread2 = new Thread(runnable1); thread2.start();
(2) 无参无返回值的lambda表达式
package com.wt.practice.lx01;
/**
* @Author wt
* @Date 2022/7/19 19:06
* @PackageName:com.wt.practice.lx01
* @ClassName: Demo02
* @Description: 无参无返回值
* @Version 1.0
*/
public class Demo02 {
public static void main(String[] args) {
Swrimable swrimable1 = new Swrimable() {
@Override
public void Swrimming() {
System.out.println("这是使用匿名内部类的方式实现");
}
};
fun(swrimable1);
Swrimable swrimable = ()-> System.out.println("这是使用lambda表达式");
fun(swrimable);
}
public static void fun(Swrimable swrimable){
swrimable.Swrimming();
}
}
//匿名式接口
interface Swrimable{
public void Swrimming();
}
(3) 练习有参数有返回值的Lambda
下面举例演示 java.util.Comparator 接口的使用场景代码,其中的抽象方法定义为:
-
public abstract int compare(T o1, T o2);
当需要对一个对象集合进行排序时, Collections.sort 方法需要一个 Comparator 接口实例来指定排序的规则。
package com.wt.practice.lx01;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @Author wt
* @Date 2022/7/19 19:13
* @PackageName:com.wt.practice.lx01
* @ClassName: Demo03
* @Description: 有参有返回值
* @Version 1.0
*/
public class Demo03 {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("阮籍",42,178));
list.add(new Person("阮咸",32,190));
list.add(new Person("向秀",25,175));
list.add(new Person("刘伶",19,187));
//对集合中的元素进行排序,
//传统做法,定义匿名接口
Comparator<Person> comparator = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
};
Collections.sort(list,comparator);
for (Person xx:list){
System.out.println(xx);
}
System.out.println("===============");
//使用lambda表达式进行排序
Comparator<Person> comparator1 = (Person o1,Person o2)->{
return o1.getAge()- o2.getAge();
};
Collections.sort(list,comparator1);
for (Person xx:list){
System.out.println(xx);
}
System.out.println("================");
//使用更简洁的lambda表达式排序
Comparator<Person> comparator2 = (o1, o2) -> o2.getAge()- o1.getAge();
Collections.sort(list,comparator2);
for (Person xx:list){
System.out.println(xx);
}
}
}
class Person{
private String name;
private int age;
private int height;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public Person(String name, int age, int height) {
this.name = name;
this.age = age;
this.height = height;
}
}
(4) 详细介绍lambda表达式
2.函数式接口
**内置函数式接口的由来**
public class Test03 {
public static void main(String[] args) {
Operater o=arr -> {
int sum=0;
for(int n:arr){
sum+=n;
}
System.out.println("数组的和为:"+sum);
};
fun(o);
}
public static void fun(Operater operater){
int[] arr={2,3,4,5,6,7,11};
operater.getSum(arr);
}
}
@FunctionalInterface
interface Operater{
//求数组的和
public abstract void getSum(int[] arr);
}
分析
我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方法名,只关心抽 象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。
常见的函数式接口
(1) Consumer<T>消费型接口
方法:accept()
有参数,无返回值
package com.wt.practice.lx02;
import java.util.function.Consumer;
/**
* @Author wt
* @Date 2022/7/19 19:45
* @PackageName:com.wt.practice.lx02
* @ClassName: ConsumerImpl
* @Description: Consumer 有参数无返回值
* @Version 1.0
*/
public class ConsumerImpl {
public static void main(String[] args) {
//t指向调用的方法中的参数money
Consumer<Double> consumer = t-> System.out.println("吃饭花费了"+t);
fun(consumer,200.0);
}
//调用某个方法时,该方法需要的参数为接口类型,
//参数money的数据类型需要与接口类型的泛型相同
public static void fun(Consumer<Double> consumer,Double money){
consumer.accept(money);
}
}
(2) Supplier<T>供给型函数式接口
T:表示返回结果的泛型
方法 :get();
无参,想有返回结果的函数式接口时
package com.wt.practice.lx02;
import java.util.Random;
import java.util.function.Supplier;
/**
* @Author wt
* @Date 2022/7/19 19:50
* @PackageName:com.wt.practice.lx02
* @ClassName: SupplierImpl
* @Description: 无参可以指定返回值(四类八种及引用类型)
* @Version 1.0
*/
public class SupplierImpl {
public static void main(String[] args) {
//可以返回四类八种基本类型,及引用类型
Supplier supplier = ()-> "dfdf";
fun(supplier);
System.out.println("==============");
//返回[0-10)的随机数
Supplier supplier1 = ()->new Random().nextInt(10);
fun(supplier1);
}
public static void fun(Supplier supplier){
Object o = supplier.get();
System.out.println("指定返回的数据是:"+o);
}
}
(3) Function<T,R> 函数型函数式接口
T: 参数类型的泛型
R: 函数返回结果的泛型
方法:apply()
有参,有返回值
package com.wt.practice.lx02;
import java.util.Locale;
import java.util.function.Function;
/**
* @Author wt
* @Date 2022/7/19 20:04
* @PackageName:com.wt.practice.lx02
* @ClassName: FunctionImpl
* @Description: Function 有参有返回值
* @Version 1.0
*/
public class FunctionImpl {
public static void main(String[] args) {
//将输入的参数转换为大写
Function<String,String> function = t->t.toUpperCase(Locale.ROOT);
//计算输入参数的长度(包含空格)
Function<String,Integer> function1 = t2-> t2.length();
fun(function,"Hello word");
fun1(function1,"Hello word");
//直接将lambda表达式写入方法中
fun2(t->t.toUpperCase(Locale.ROOT),"hgji");
}
public static void fun(Function<String,String> function,String prin){
String apply = function.apply(prin);
System.out.println(apply);
}
public static void fun1(Function<String,Integer> function,String prin){
Integer apply = function.apply(prin);
System.out.println(apply);
}
public static void fun2(Function<String,String> function,String prin){
String apply = function.apply(prin);
System.out.println(apply);
}
}
(4) Predicated<T>
T: 参数的泛型
方法: test()
当传入一个参数时,需要对该参数进行判断时,则需要这种函数
package com.wt.practice.lx02;
import java.util.function.Predicate;
/**
* @Author wt
* @Date 2022/7/19 20:12
* @PackageName:com.wt.practice.lx02
* @ClassName: PredicateImpl
* @Description: Predicate 当传入一个参数时,需要对该参数进行判断时,则需要这种函数
* @Version 1.0
*/
public class PredicateImpl {
public static void main(String[] args) {
//返回类型为boolean
fun(t->t>3?true:false,5);
}
public static void fun(Predicate<Integer> predicate,Integer num){
boolean test = predicate.test(num);
System.out.println(test);
}
}
3.方法引用
(1) lambda表达式的冗余
package com.wt.practice.lx03;
import java.util.function.Consumer;
/**
* @Author wt
* @Date 2022/7/19 20:20
* @PackageName:com.wt.practice.lx03
* @ClassName: Demo01
* @Description: TODO
* @Version 1.0
*/
public class Demo01 {
public static void main(String[] args) {
//第一种方法
Consumer<Integer[]> consumer = (arr)->{
int sum=0;
for (Integer xx:arr){
sum+=xx;
}
System.out.println(sum);
};
fun(consumer);
//第二种:直接调用类中所拥有的方法,防止造成代码冗余
fun(Demo01::sum);
}
public static void fun(Consumer<Integer[]> consumer){
Integer[] arr = {1,2,3,4,5};
consumer.accept(arr);
}
public static void sum(Integer[] arr){
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum+=arr[i];
}
System.out.println("总和是:"+sum);
}
}
请注意其中的双冒号 :: 写法,这被称为“方法引用”,是一种新的语法
(2) 什么是方法引用
方法引用的分类
(2).实例方法的引用
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。
package com.wt.practice.lx03;
import java.util.function.Consumer;
/**
* @Author wt
* @Date 2022/7/19 21:40
* @PackageName:com.wt.practice.lx03
* @ClassName: Demo02
* @Description: 实例方法引用
* @Version 1.0
*/
public class Demo02 {
public static void main(String[] args) {
//先创建对象,再通过对象调用方法
Computer computer = new Computer();
fun(computer::sum);
}
public static void fun(Consumer<Integer[]> consumer){
Integer[] arr = {1,2,3,4,5};
consumer.accept(arr);
}
}
//创建对象
class Computer{
//定义求和方法
public void sum(Integer[] arr){
int sum = 0;
for (Integer xx:arr){
sum+=xx;
}
System.out.println("总和是"+sum);
};
}
(3) 对象方法引用
public class Demo03 {
public static void main(String[] args) {
/**
* 对象方法引用 类名::实例方法 (参数1,参数2)->参数1.实例方法(参数2)
*/
//Function<String,Integer> function = t->t.length();
Function<String,Integer> function = String::length;
Integer hello = function.apply("hello");
System.out.println(hello);
//比较两个字符串是否一致
//BiFunction<String,String,Boolean> biFunction = (t1,t2)->t1.equals(t2);
BiFunction<String,String,Boolean> biFunction = String::equals;
Boolean apply = biFunction.apply("hello", "hello");
System.out.println(apply);
}
}
(4) 构建方法引用
package com.wt.pratice.lx01;
import java.util.function.Function;
/**
* @Author wt
* @Date 2022/7/21 9:59
* @PackageName:com.wt.pratice.lx01
* @ClassName: Demo04
* @Description: 构造方法引用 类名::new
* @Version 1.0
*/
public class Demo04 {
public static void main(String[] args) {
//Function<String,People> function = t->new People(t);
Function<String,People> function = People::new;
People q = function.apply("千奈");
System.out.println(q);
}
}
class People{
private String name;
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
'}';
}
public People(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
二.Stream流
Java8的两个重大改变,一个是Lambda表达式,另一个就是Stream API表达式。==Stream 是Java8中处理集合的关键抽象概念==,它可以对集合进行非常复杂的查找、过滤、筛选等操作.
1.为什么使用Stream流
当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。我们来体验 集合操作数据的弊端,需求如下:
一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰,何线程
需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
传统写法
public class My {
public static void main(String[] args) {
// 一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰
// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
// 1.拿到所有姓张的
ArrayList<String> zhangList = new ArrayList<>(); // {"张无忌", "张强", "张三丰"}
for (String name : list) {
if (name.startsWith("张")) {
zhangList.add(name);
}
}
// 2.拿到名字长度为3个字的
ArrayList<String> threeList = new ArrayList<>(); // {"张无忌", "张三丰"}
for (String name : zhangList) {
if (name.length() == 3) {
threeList.add(name);
}
}
// 3.打印这些数据
for (String name : threeList) {
System.out.println(name);
}
}
}
分析:
这段代码中含有三个循环,每一个作用不同:
-
首先筛选所有姓张的人;
-
然后筛选名字有三个字的人;
-
最后进行对结果进行打印输出。
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环 是做事情的方式,而不是目的。每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,只能再使 用另一个循环从头开始。
那Stream能给我们带来怎样更加优雅的写法呢?
public class Demo01 {
public static void main(String[] args) {
// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
List<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰","何线程");
list.stream()
.filter(t->t.startsWith("张"))
.filter(t->t.length()==3)
.forEach(System.out::println);
}
}
对集合的操作语法简洁:性能比传统快。
2. Stream流的原理
注意:Stream和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象!
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种==数据结构==,==不保存数据==,而是对数据进行==加工 处理==。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
Stream不存在数据,只对数据进行加工处理。
3. 如何获取Stream流对象
public class Demo02 {
public static void main(String[] args) {
//串行流
//通过集合对象调用stream()获取流
List<String> list = new ArrayList<>();
Collections.addAll(list,"阮咸","向秀","刘伶","小谢");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
System.out.println("=========");
//通过数组工具类获取stream()对象
int[] arr = {1,2,3,4,5};
IntStream stream1 = Arrays.stream(arr);
//使用Stream类中of方法调用
Stream<String> hello = Stream.of("hello", "world");
//通过Stream的方法调用
LongStream range = LongStream.range(1, 10);
//range.forEach(t-> System.out.println(t));
//并行流--->如果流中的数据量足够大,并行流可以加快处速度
Stream<String> stringStream = list.parallelStream();
stringStream.forEach(t-> System.out.println(t));
}
}
4. Stream流中常见的api
数据源
public class Demo03 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("向天笑",20,"中国",'M'));
personList.add(new Person("李康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("李康",22,"中国",'M'));
}
}
class Person {
private String name;
private Integer age;
private String country;
private char sex;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", country='" + country + '\'' +
", sex=" + sex +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public Person(String name, Integer age, String country, char sex) {
this.name = name;
this.age = age;
this.country = country;
this.sex = sex;
}
}
(1)filter / foreach / count
//找到年龄大于18且国家为中国的人
personList.stream()
.filter(t->t.getSex()>18)
.filter(t->t.getCountry().equals("中国"))
.forEach(System.out::println);
System.out.println("============");
//输出性别为F的人
personList.stream()
.filter(t->t.getSex()=='F')
.forEach(System.out::println);
(2) map | sorted
map--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
//对流中元素排序
personList.stream()
.sorted((o1,o2)->o1.getAge()-o2.getAge())
.forEach(System.out::println);
//集合中每个元素只要名.map--->原来流中每个元素转换为另一种格式。
// personList.stream()
// .map(item->{
// Map<String,Object> m=new HashMap<>();
// m.put("name",item.getName());
// m.put("age",item.getAge());
// return m;
// })
// .forEach(System.out::println);
(3) min max
//获取员工中年龄最大的人
Optional<Person> max = personList.stream()
.max(((o1, o2) -> o1.getAge() - o2.getAge()));
System.out.println(max);
//获取员工中年龄最小的人
Optional<Person> min = personList.stream()
.min(((o1, o2) -> o1.getAge() - o2.getAge()));
System.out.println(min);
(4)规约reduce
/**
* 规约reduce
* */
//求所有人年龄的和
Optional<Integer> reduce = personList.stream()
.map(t -> t.getAge())
.reduce((a, b) -> a + b);
System.out.println(reduce.get());
//给a设置初始值(默认为0)
Integer reduce1 = personList.stream()
.map(t -> t.getAge())
.reduce(10, (a, b) -> a + b);
System.out.println(reduce1);
(5)collect搜集 match find
/**
* collect 收集 match find
*/
//收集方法 collect
//年龄大于20且性别为M
List<Person> collect = personList.stream()
.filter(t -> t.getAge() > 20)
.filter(t -> t.getSex() == 'M')
.collect(Collectors.toList());
System.out.println(collect);
System.out.println("-------------");
personList.stream()
.filter(t->t.getAge()>20)
.filter(t->t.getSex()=='M')
.forEach(System.out::println);
(6) 条件判断
//判断是否满足条件
//查询所有性别为F的人的年龄是否存在大于20的,所有都满足则返回true,
boolean b = personList.stream()
.filter(t -> t.getSex() == 'F')
.allMatch(t -> t.getAge()>= 20);
System.out.println(b);
//查询所有性别为F的人的年龄是否存在大于20的,存在一个即可返回true,
boolean b1 = personList.stream()
.filter(t -> t.getSex() == 'F')
.anyMatch(t -> t.getAge() >= 20);
System.out.println(b1);
//都不满足则为true,否则为false
boolean b2 = personList.stream()
.filter(t -> t.getSex() == 'F')
.noneMatch(t -> t.getAge() >= 24);
System.out.println(b2);
5.新增了日期时间类
旧的日期时间的缺点:
设计比较乱: Date日期在java.util和java.sql也有,而且它的时间格式转换类在java.text包。
线程不安全。
新增加了哪些类?
LocalDate: 表示日期类。yyyy-MM-dd
LocalTime: 表示时间类。 HH:mm:ss
LocalDateTime: 表示日期时间类 yyyy-MM-dd t HH:mm:ss sss
DatetimeFormatter:日期时间格式转换类。
Instant: 时间戳类。
Duration: 用于计算两个日期类
public class Demo01 {
public static void main(String[] args) {
//获取当前日期
LocalDate now = LocalDate.now();
System.out.println(now);
//指定日期
LocalDate of = LocalDate.of(2022, 7, 20);
System.out.println(of);
//获取当前时间
LocalTime nowtime = LocalTime.now();
System.out.println(nowtime);
//指定时间
LocalTime oftime = LocalTime.of(21, 23, 23, 20);
System.out.println(oftime);
//获取当前日期时间
LocalDateTime nowdatetime = LocalDateTime.now();
System.out.println(nowdatetime);
//指定日期时间
LocalDateTime ofdatetime = LocalDateTime.of(2022, 6, 6, 22, 34, 34, 23);
System.out.println(ofdatetime);
//计算两个时间类的差
Duration between = Duration.between(ofdatetime,nowdatetime);
System.out.println("相差天数:"+between.toDays()+"天");
System.out.println("相差小时:"+between.toHours()+"小时");
//日期格式转换
//1.定义日期格式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
//2.将字符串转换为日期格式
LocalDate parse = LocalDate.parse("2001-12-28", dateTimeFormatter);
System.out.println("字符串转日期:"+parse);
}
}