函数式编程思想
注重“函数” 函数实现的结果, 以及它必须有 接口
以前: 接口 对象 = new 实现类() ;
现在: 接口 对象 = new 接口() {
实现接口的抽象方法
}
JDK1.8新特性
1.lambda表达式
属于jdk的语法塘
分为三部分
(参数列表 参数名) -> {抽象方法的实现体}
①参数列表 当参数列表只有一个参数的时候,()可以省略,但是没有参数的时候,()不能省略; 参数类型可以不写
②-> 中间不能有空格
③{方法 的实现体}:当方法只有一句话时候,{}可以省略,但是当有return时候,{}必须存在,但是只有一句表达式的时候,return可以省略,这时候{}可以省略
补充:参数类型可以省略,但是参数类型必须一致,可以运用泛型
List c = Arrays.asList(12, 15, 11, 2, 5, 3, 6, 8, 42, 8);
/**
* Lambda语法
* 接口中有且只有一种方法
*/
public class Test1 {
public static void main(String[] args) {
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
};
//简写
Runnable runnable1=()->{ System.out.println("hello");};
//再次简写
Runnable runnable2=()->{int a=8;
System.out.println(a);};
System.out.println();;
new Thread(runnable1).start();
new Thread(runnable2).start();
List<Integer> c = Arrays.asList(12, 15, 11, 2, 5, 3, 6, 8, 42, 8);
// c.sort((o1,o2)->o1.compareTo(o2));//==》c.sort(list);
// for (Integer integer : c) {
// System.out.println("-->"+integer);
// }
c.forEach((i) -> System.out.println("-->"+i));
c.stream().forEach(System.out::println);
}
}
**函数式接口**
lambda表达式就是实现SAM接口的语法糖(只有一个抽象接口需要实现,但是可以继承和包含其他的非抽象方法)
@FunctionalInterface注解会强制该接口只有一个非抽象方法
使用lambda有一些必要前提:
①必须使接口
②接口中有且只有一个抽象方法
1.自定义函数接口
eg:需求: 定义一个计算器接口,用于 计算两个数 , 该接口既可以 计算两个数之和、 两个数之差、两个数之乘积 ,两个数之商
@FunctionalInterface
public interface Calc {
public int calc(int a,int b);
}
import org.junit.Test;
public class Test1 {
@Test
public void test1(){
Calc calc=(a,b)->a+b;
int calc1 = calc.calc(5, 6);
System.out.println(calc1);
}
@Test
public void test2(){
Calc calc=(a,b)->a-b;
int calc1 = calc.calc(5, 6);
System.out.println(calc1);
}
@Test
public void test3(){
Calc calc=(a,b)->a*b;
int calc1 = calc.calc(5, 6);
System.out.println(calc1);
}
@Test
public void test4(){
Calc calc=(a,b)->a/b;
int calc1 = calc.calc(5, 6);
System.out.println(calc1);
}
}
补充:lambda可以省略的原因使规定了接口有且只有一个抽象方法
2.消费性接口
接口 : Consumer void accept(T t)
解读:无返回值,但是要传递一或两个个泛型(任意类型但是不包括基本类型,基本类型的泛型为包装类) Consumer ----》为接口名 accept---》为接口里面的抽象方法
/**
* 1、消费型接口
* Consumer void accept(T t)
*/
@Test
public void Test1(){
Consumer<Integer> consumer=(a)->{
int num = 0;
for (int i = 0; i <= a; i++) {
num+=i;
}
System.out.println(num);
};
consumer.accept(100);
}
/**
*
* Consumer void accept(T t,U u)
*/
@Test
public void Test2(){
BiConsumer<String,Integer> biConsumer =new BiConsumer<String, Integer>() {
@Override
public void accept(String s, Integer score) {
System.out.println("亲爱的"+s+",分数:"+score);
}
};
BiConsumer<String,Integer> biConsumer1=(s,score)-> System.out.println("亲爱的"+s+",分数:"+score);
biConsumer1.accept("baby",100);
}
3.供给型接口
接口:Supplier T get()
解读:有返回值,无参数
/**
* 2、供给型接口
* Supplier T get() : 有返回值 无参数
*/
@Test
public void Test3(){
Supplier<Integer> supplier=()->{return (int)(Math.random()*101);};
//省略
Supplier<Integer> supplier1=()->(int)(Math.random()*101);
Integer integer = supplier1.get();
System.out.println(integer);
}
4.判断型接口
接口: Predicate boolean test(T t)
解读:有一个或两个参数,有返回值但是返回值为boolean类型
**
* 判断型接口
* Predicate boolean test(T) 判断是否满足 ,返回true/false
*/
@Test
public void Test4(){
// 判断一个字符串的长度是否大于5
Predicate<String> predicate =new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length()>5;
}
};
//简写
Predicate<String> predicate1 =s->s.length()>5;
System.out.println(predicate1.test("sda"));
}
/**
* BiPredicate boolean test(T t, U u)
*/
@Test
public void Test5(){
// 判断一个字符串是否包含另一个字符串
BiPredicate<String,String> biPredicate=new BiPredicate<String, String>() {
@Override
public boolean test(String s, String s2) {
return s.contains(s);
}
};
//简写
BiPredicate<String,String> biPredicate1=(s,s1)->s.contains(s1);
System.out.println(biPredicate1.test("dada", "da"));
}
// 很多时候 是将 这个接口作为一个方法的参数来使用 ,
@Test
public void test8(){
boolean flag = myTest("www.atguigu.com" ,"atguigu" , (s1,s2) -> s1.contains(s2));
System.out.println(flag);
}
/**
* 将接口的类型 作为方法的 形参
* @param biPredicate
* @return
*/
public static boolean myTest(String s1,String s2, BiPredicate<String , String > biPredicate){
if(biPredicate.test(s1,s2)){
System.out.println("条件满足");
return true;
}else{
System.out.println("条件不满足");
return false;
}
}
}
5. 功能型接口(api--》接口)
接口:Functio(T,R) R apply(T t)
解读:第一个泛型T是方法的参数,第二个参数是返回值类型
接口:BiFunction(T,U,R) R apply(T t,U u)
解读:第一个泛型T和第二个泛型U是方法的参数,第三个参数是返回值类型
/**
* 功能型接口
* Function(T,R) R test(T)
* 第一个泛型T是方法的参数,第二个参数是返回值类型
*/
@Test
public void Test6(){
// 判断一个字符串的长度
Function<String, Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
};
//简写
Function<String,Integer> function1=(s)->s.length();
System.out.println(function1.apply("babt love you"));
}
// 运用 把接口作为形参列表
public static int getlen(String s,Function<String,Integer> fun){
//调用接口的方法
int n =fun.apply(s);
return n;
}
@Test
public void test7(){
//genlen的参数决定了方法 实现(该繁峙县可以使用lambda表达式)
int len=getlen("baby i love you", new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
});
//简写
int len1 = getlen("baby i love you", s -> s.length());
System.out.println(len);
System.out.println(len1);
}
/**
* bifunction(U,T,R) R apply(U u,T t)
*/
@Test
public void test9(){
//eg:求两个字符串的总长度
BiFunction<String,String,Integer> biFunction = new BiFunction<String, String, Integer>() {
@Override
public Integer apply(String s, String s2) {
return s.length()+s2.length();
}
};
Integer apply = biFunction.apply("hello", "world");
System.out.println(apply);
//简写
BiFunction<String,String,Integer> biFunction1=(s,s1)->s.length()+s1.length();
Integer apply1 = biFunction1.apply("hello", "world");
System.out.println(apply1);
}
**方法引用和构造器的引用**
1.方法的引用
①类的实例名(对象名):实例方法名(没有加static修饰的方法)
/**
* 类的实例名(对象名):实例方法名(没有加static修饰的方法)
* 1.有且只有一个抽象方法
* 2.抽象方法只有一句话
* 3.这句话就是方法的调用
* 4.调用的方法的参数与抽象方法保持一致
* eg:
* public void accept(String s) {
* System.out.println(s+"hekkk"); //此时就不一致
* }
*/
@Test
public void test1(){
//eg:通过consumer接口输出helloword
Consumer<String> consumer=new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s); //方法的调用
}
};
//lambda方法
Consumer<String> consumer1=s -> System.out.println(s);
consumer1.accept("helloword");
//方法调用
//System.out 类的实例名
//println 引用的方法
Consumer<String> consumer2=System.out::println;
consumer2.accept("baby");
}
②类名::静态方法名 (加了static修饰的方法)
/**
* 类名:静态方法
* 1.有且只有一个抽象方法
* 2.抽象方法只有一句话
* 3.这句话就是方法的调用
* 4.调用的方法的参数与抽象方法保持一致
*/
@Test
public void test2(){
//eg:比较2个double类型大小 返回int
BiFunction<Double,Double,Integer> biFunction=new BiFunction<Double, Double, Integer>() {
@Override
public Integer apply(Double aDouble, Double aDouble2) {
return Double.compare(aDouble,aDouble2);//1 0 -1
}
};
biFunction.apply(5.8,7.3);
//lambda方法
BiFunction<Double,Double,Integer> biFunction1=(d1,d2)->Double.compare(d1,d2);
//方法调用
BiFunction<Double,Double,Integer> biFunction2=Double::compareTo;
}
③类名::实例方法名
/**
* 类名:实例方法名
* 1.有且只有一个抽象方法
* 2.抽象方法只有一句话
* 3.这句话就是方法的调用
* 4.抽象方法的第一个参数是方法的调用者,后面的参数与该方法的参数列表一致
*/
@Test
public void test3(){
//eg:判断一个字符串是否存在另一个字符串 contains是string类的方法
BiFunction<String,String,Boolean> booleanBiFunction=new BiFunction<String, String, Boolean>() {
@Override
public Boolean apply(String s, String s2) {
return s.contains(s2);
}
};
//lambda方法
BiFunction<String,String,Boolean> booleanBiFunction1=(s,s1)->s.contains(s1);
//方法调用
BiFunction<String,String,Boolean> booleanBiFunction2=String::contains;
System.out.println(booleanBiFunction2.apply("hello", "he"));
}
@Test
public void test4(){
//eg:比较2个double类型大小 返回int
//lambda方法
//方法调用
}
**构造方法的引用**
① 类名::new
/**
* 构造器的引用
* 类名::new
* 1.有接口(没有自定义)
* 2.抽象方法中只有一句代码的实现
* 3.该代码就是构造器的调用
* 4.构造器的参数与抽象方法的参数保持一致
*/
@Test
public void test1(){
Student baby = new Student(5, "baby");
//函数式接口
BiFunction<Integer,String,Student> function=new BiFunction<Integer, String, Student>() {
@Override
public Student apply(Integer id , String s) {
return new Student(id,s);//构造器的引用
}
};
//lambda简写
BiFunction<Integer,String,Student> function1=(id,s)->new Student(id,s);
// 构造器的引用
BiFunction<Integer,String,Student> function2=Student::new;
System.out.println(function2.apply(1, "ly"));
}
②数组类型:new
/**
* 构造器的引用
* 数组类型::new
* 1.有接口(没有自定义)
* 2.抽象方法中只有一句代码的实现
* 3.数组的的长度与抽象方法的参数保持一致
*/
@Test
public void test2(){
//eg:创建一个指定长度的int类型数组
int[] ints = new int[5];//类型为包装类 integer
//函数式接口
Function<Integer,int[]> function= new Function<Integer, int[]>() {
@Override
public int[] apply(Integer i) {
return new int[i];
}
};
//lambda简写
Function<Integer,int[]> function1=i->new int[i];
// 构造器的引用
Function<Integer,int[]> function2=int[]::new;
//调用方法的时候才赋值参数
int[] apply = function2.apply(5);
for (int i = 0; i <apply.length ; i++) {
System.out.println(apply[i]);
}
}
streamAPI
streamAPI:java.util.stream类中
功能:可以将集合数组的元素进行查找,过滤,筛选等若干处理
好处:简化程序,写更精简的,高效的代码
注意:
1.不能存数组
2.不会改变源数据,每次操作后会返回一个新的stream流
3.stream流是延迟操作,只有出现结果的时候,才会执行过程
stream流分为三步:
1.创建流
2.中间处理
3.最终操作
public class Test1 {
public static void main(String[] args) {
List<String> list = Arrays.asList("hsd", "addsa", "fasda", "dasdua", "hello");
//创建流
Stream<String> stream = list.stream();
//中间操作 (会得到新的流,流操作后会自动关闭)
Stream<String> stringStream = stream.filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length() > 4;
}
});
//最终操作 //消费型接口
stringStream.forEach(s -> System.out.println(s));
//jdk8可以采取链式操作(中间操作不断,流不会终止和产生新的)
long count = list.stream()//开始操作 只能一个
.filter(s -> s.length() > 4) //中间操作 可以很多个
.count();//最终操作 只能有一个
}
}
练习:
/**
- 需求:通过streamAPI 查询年龄在35岁以上的
- 需求:查询35随以上的,工资8000
- 需求:查询工资20000 35岁以上 火箭1
*/
/**
* 需求:通过streamAPI 查询年龄在35岁以上的
* 需求:查询35随以上的,工资8000
* 需求:查询工资20000 35岁以上 火箭1
*/
public class Test1 {
@Test
public void Test1(){
List<Emyee> list = Arrays.asList(
new Emyee(1,"s",10000,18,"火箭1"),
new Emyee(2,"s1",100000,25,"火箭2"),
new Emyee(3,"s2",1000000,35,"火箭3"),
new Emyee(4,"s3",10000,45,"火箭2"),
new Emyee(5,"s4",10000,55,"火箭1"),
new Emyee(6,"s5",100000,33,"火箭2"),
new Emyee(7,"s6",10000,22,"火箭3")
);
// 1、 创建流
Stream<Emyee> stream =list.stream();
//中间操作
Stream<Emyee> stream1 =stream.filter(new Predicate<Emyee>() {
@Override
public boolean test(Emyee e) {
return e.getAge()>35;
}
});
stream1.forEach(System.out::println);
list.stream()
.filter(emyee -> emyee.getAge()>35 &&emyee.getSalary()>8000)
// .forEach(System.out::println);
.forEach(emyee-> System.out.println(emyee.getName()));
list.stream()
.filter(emyee -> emyee.getAge()>35 &&emyee.getSalary()>8000 &&emyee.getSection().equals("火箭1"))
.forEach(System.out::println);
}
}
public class Emyee {
private int id;
private String name;
private double salary;
private int age;
private String section;
public Emyee() {
}
public Emyee(int id, String name, double salary, int age, String section) {
this.id = id;
this.name = name;
this.salary = salary;
this.age = age;
this.section = section;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSection() {
return section;
}
public void setSection(String section) {
this.section = section;
}
@Override
public String toString() {
return "Emyee{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", age=" + age +
", section='" + section + '\'' +
'}';
}
}
stream流三步的主要方法
①创建流的方法:
1.顺序流
2.并列流
3.数组创建流
4.stream of创建流
创建无限流(2种方法)
/**
* 创建流stream
*/
public class Test1 {
/**
* 创建顺序流 和并行流
*/
@Test
public void test1(){
List<String> list = Arrays.asList("jaa", "sada", "jafafa");
//创建顺序流
Stream<String> stream = list.stream();
//创建并行流 (由多个线程创建一个stream对象)
//数据量大可以用并行流 该流的输出顺序不一定
Stream<String> stream1 = list.parallelStream();
stream.forEach(System.out::println);
System.out.println();
stream1.forEach(System.out::println);
}
/**
* 创建数组 流
*/
@Test
public void test2(){
String[] list = {"jaa", "sada", "jafafa"};
//数组创建流
Stream<String> stream = Arrays.stream(list);
stream.forEach(System.out::println);
}
/**
* stream of(T。。。) 创建流
*/
@Test
public void test3(){
//数组创建流
Stream<String> stream = Stream.of("jaa", "sada", "jafafa");
long count = stream.filter(s -> s.length() > 1).count();
System.out.println("元素个数:"+count);
}
/**
* 创建一个无限流
*/
@Test
public void test4(){
//数组创建流
Stream.generate(new Supplier<Double>() {
// 返回一个double类型的随机数
@Override
public Double get() {
return Math.random();
}
});
//? 源源不断的产生数据
Stream<Double> generate = Stream.generate(() -> Math.random());
generate.forEach(System.out::println);
}
/**
* 创建一个无限流
*/
@Test
public void test5(){
//数组创建流
Stream<Integer> iterate = Stream.iterate(0, new UnaryOperator<Integer>() {
@Override
public Integer apply(Integer i) {
return i += 1;
}
});
//? 源源不断的产生数据
iterate.forEach(System.out::println);
}
}
②中间操作
/**
* stream流的中间操作
*/
public class Test1 {
@Test
public void teat1(){
List<String> list = Arrays.asList("sdadaada","java", "jdbc", "mysql", "servlet", "maven","java","java");
list.stream()
.filter(s-> s.length()>2)//判断字符个数
// .sorted()//判断数组的排列
.sorted(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
})
.skip(1)//跳过一个
.limit(2)//取前两个
.peek(s -> System.out.println("study="+s)) //这里的s与20行的s相对应 表示修饰哪些 不会改变元素本身
// .forEach(s->System.out::println);
.forEach(s->System.out.println());
// .distinct() 自动过滤重复的元素
list.stream().distinct().forEach(System.out::println);
// map 对元素进行处理,并返回处理结果
list.stream().map(new Function<String, Integer>() {//会改变元素本身
@Override
public Integer apply(String s) {
return s.length();
}
}).forEach(System.out::println);
list.stream().map(s-> "元素:"+s).forEach(System.out::println);
//map和peek的区别:
//map(Function)将处理的结果放入流中
//peek(Consumer)知识处理过程,没有返回值,不会将结果放入stream流中
}
@Test
public void test2(){
List<String> list = Arrays.asList("sdadaada","java", "jdbc", "mysql", "servlet", "maven","java","java");
// 根据flatMap的实现,将元素先拆分成指定的一段流,最后每一个元素的流数据合并一个流
list.stream().flatMap(new Function<String, Stream<String>>() {
@Override
public Stream<String> apply(String s) {
return Arrays.stream(s.split("a"));//由于split拆分的返回的是数组 ---》转化为流
}
}).forEach(System.out::println);
}
}
③最终操作
/**
* 最终操作
*/
public class Test1 {
/**
* allMatch 检查元素是否都匹配
*/
@Test
public void test1(){
List<Integer> list = Arrays.asList(1, 8, 6, 3, 2, 4, 5);
//返回该元素是否全都小于5
boolean b = list.stream().allMatch(n -> n <= 6);
System.out.println(b);
}
/**
* antMatch 检查元素是否由一个满足
*/
@Test
public void test2(){
List<Integer> list = Arrays.asList(1, 8, 6, 3, 2, 4, 5);
//返回该元素是否全都小于5
boolean b = list.stream().anyMatch(n -> n <= 6);
System.out.println(b);
}
/**
* onneMatch 检查元素是否没有匹配
*/
@Test
public void test3(){
List<Integer> list = Arrays.asList(1, 8, 6, 3, 2, 4, 5);
//返回该元素是否全存在偶数 (存在一个为false)
boolean b = list.stream().noneMatch(n -> n%2==0);
System.out.println(b);
}
/**
*findFirst() 返回第一个元素
*/
@Test
public void test4(){
List<Integer> list = Arrays.asList(1, 8, 6, 3, 2, 4, 5);
//返回值Optional :用于封装返回值的一个工具 可避免空指针异常
Optional<Integer> first = list.stream().findFirst();
System.out.println(first);//Optional.empty为空
// System.out.println(first.get());//返回第一个元素
System.out.println(first.isPresent());//判断元素是否存在
}
/**
*findAny() 返回任意一个元素
*/
@Test
public void test5(){
List<Integer> list = Arrays.asList(1, 8, 6, 3, 2, 4, 5);
//返回值Optional :用于封装返回值的一个工具 可避免空指针异常
Optional<Integer> first = list.stream().findAny();
System.out.println(first);//Optional.empty为空
System.out.println(first.get());//返回first中的一个元素
System.out.println(first.isPresent());//判断元素是否存在
}
/**
*max() 最大的元素
* min() 最小的元素
*/
@Test
public void test6(){
List<Integer> list = Arrays.asList(1, 8, 6, 3, 2, 4, 5);
Optional<Integer> max = list.stream().max((o1, o2) -> o1.compareTo(o2));
Optional<Integer> min = list.stream().min((o1, o2) -> o1.compareTo(o2));
System.out.println(max);
System.out.println(min);
}
/**
*reduce() 求和
*/
@Test
public void test7(){
List<Integer> list = Arrays.asList(1, 8, 6, 3, 2, 4, 5);
Optional<Integer> reduce = list.stream().reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer a, Integer b) {
return a + b;
}
});
//可以设置初始值
Integer reduce1 = list.stream().reduce(10, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer a, Integer b) {
return a + b;
}
});
System.out.println(reduce);
System.out.println(reduce1);
}
/**
* 遍历 计算总数 如果需要将中间操作的结果进行接收 可以用collect()
* 可将处理的流收集成 集合类型
*/
@Test
public void test8(){
List<String> list = Arrays.asList("sdadaada","java", "jdbc", "mysql", "servlet", "maven","java","java");
List<String> a = list.stream().flatMap(new Function<String, Stream<String>>() {
@Override
public Stream<String> apply(String s) {
return Arrays.stream(s.split("a"));//由于split拆分的返回的是数组 ---》转化为流
}
}).collect(Collectors.toList());//收集成新的集合
System.out.println("新的集合:" + a);
}
}
知识补充:
optiona类型
public class TestOptional {
public static void main(String[] args) {
// Optional避免空指针异常
Employee emp = null;
// System.out.println(emp.getName()); // 空指针
// of : 将一个对象封装成 option
Optional<Employee> optional = Optional.of(null);
// 判断对象是否存在
if(optional.isPresent()){
System.out.println(optional.get());
}else{
System.out.println("不存在");
}
// orElse(obj) : 如果对象为空则使用obj初始化
}
}