Lambda表达式
- 为匿名内部类提供了简便写法
了解内部类的使用
public static void main1(String[] args) {//在方法内部定义内部类class MyClass{public void say(){System.out.printf("我是内部类");}}//使用内部类创建对象MyClass myClass = new MyClass();myClass.say();//简便写法new MyClass().say();}
优化成匿名内部类
必须继承类,或者是实现接口
public interface MyInferface1 {void run();}
在代码中使用创建对象
public static void main2(String[] args) {//匿名内部类//匿名内部类,必须继承其他类或者是实现某个接口MyInferface1 myInferface1 = new MyInferface1() {@Overridepublic void run() {System.out.println("我是匿名内部类");}};myInferface1.run();/////省略引用new MyInferface1(){@Overridepublic void run() {System.out.println("我是匿名内部类2");}}.run();}
lambda表达的优化
必须是接口,并且只有一个抽象方法
public interface MyInferface1 {void run();}
必须通过引用接收创建的对象
public static void main3(String[] args) {//lambda表达式对匿名内部类的优化//1,必须是接口//2, 只能有一个未实现的方法MyInferface1 myInferface1 = ()->{System.out.println("lambda表达式");};myInferface1.run();}
主要将接口当作参数传入到其他的方法中(用于模拟函数的引用传递)
定义接口
public interface MyForEachInterface {void each(Object o);}
定义调用方法
//实现了一个方法,可以遍历集合public static void myForEach(List list,MyForEachInterface eachInterface){for(int i=0;i<list.size();i++){//调用了传递进入来的接口的方法eachInterface.each(list.get(i));}}
使用这个myForEach方法的时候,通过lambda表达式传递方法引用
public static void main(String[] args) {List<Integer> list = new ArrayList<>();for (int i=0;i<10;i++){list.add(i);}//使用lambda表达式遍历list.forEach(obj->{System.out.println("打印方式1"+obj);});myForEach(list,obj->{System.out.println("打印方式2"+obj.toString());});}
函数接口
在java中是不能够将方法作为一个参数引用传递到其他的方法中,但是c或者是js都是可以的
- 在java中将只有一个方法的接口称为函数接口,然后在利用lambda表达式,让接口的传递看上去是在传递函数
为了保证函数接口的规范,jdk1.8推出一些规则
在接口上使用@FunctionalInterface,可以检测当前接口是否是函数接口
@FunctionalInterfacepublic interface MyInterface2 {void accept();}
为了方便使用,jdk提供一些自带的接口
在lambda表达式上的进一步简化
//使用lambda表达式输出list.forEach(integer -> {System.out.println(integer);});//使用方法引用进一步简化list.forEach(System.out::println);
类::静态方法,一般用于静态方法
对象::实例方法,一般用于对象直接调用方法(传入的对象,被作为参数被省略的方法引用作为参数调用)
list.forEach(s->{System.out.println(s);});list.forEach(System.out::println);
类::实例方法,这个实例方法是通过调用类代替对象调用的(调用传入对象的方法)
在类实现的一个非static方法
public void say(){System.out.println("我是"+this.getName()+",今年"+this.getAge());}
在使用lambda表达式的时候使用传入的参数对象,调用say方法
list.forEach(s->{s.say();});
在省略参数的时候,由于没有可以调用方法的媒介,那么可以直接使用类进行调用
list.forEach(Student::say);
类::new 对象
- 当返回结果是一个对象的时候,并且函数体是创建对象,那么可以使用方法引用省略
在调用的时候,构造返回的参数要和接口应用的参数保持一致 ```java public static void main(String[] args) { //匿名内部类的写法 wrap(new Supplier
() { @Override public String get() { return new String();
} }); //lambda表达式 wrap(()->{ return new String(); }); //进一步简化 wrap(()-> new String());
//使用引用简化 wrap(String::new); }
public static void wrap(Supplier
supplier){ System.out.println(“包装开始”); String result = supplier.get(); System.out.println(“获取包装结果”); 简化小结
匿名内部类
wrap("12", new Function<String, Object>() {@Overridepublic Object apply(String s) {return new Short(s);}});
匿名内部类,在实现接口的情况下,并且接口之后一个方法,那么则可以简化成lambda表达式
wrap("15", s -> {return new Integer(s);});
lambda表达式中,如果之后一行代码,那么{}可以被省略,并且如果带返回结果的话,return也会省略
wrap("15", s -> new Integer(s));
如果调用的一行代码,参数和返回与接口的抽象方法,保持一致,那么则可以使用方法引用
wrap("15",Integer::new);
Stream
流可以对集合进行筛选,快速的获取想要的结果
如果没有流,那么要遍历多次,并且还需要创建其他的集合来辅助操作
public static void main2(String[] args) {List<String> list = new ArrayList<>();Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据//获取所有姓张的List<String> nameList = new ArrayList<>();for (int i=0;i<list.size();i++){String name = list.get(i);if(name.contains("张")){nameList.add(name);}}//在原有的基础上再次进行遍历List<String> lengthList = new ArrayList<>();for (int i=0;i<nameList.size();i++){String name = nameList.get(i);if(name.length()==3){lengthList.add(name);}}//将结果打印for (int i=0;i<lengthList.size();i++){String name = lengthList.get(i);System.out.println(name);}}
使用流简化操作
public static void main(String[] args) {List<String> list = new ArrayList<>();Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据//获取流Stream<String> stream = list.stream();//过滤获取所有姓名张的/*stream.filter(new Predicate<String>() {//过滤方法@Overridepublic boolean test(String o) {if(o.contains("张")){return true;}return false;}}).filter(new Predicate<String>() {//筛选@Overridepublic boolean test(String s) {if(s.length()==3){return true;}return false;}}) .forEach(s->{//终结遍历方法System.out.println(s);});*///过滤方法//筛选stream.filter(o -> o.contains("张")).filter(s ->s.length()==3).forEach(System.out::println);}
流的特点
- Stream 自己不会存储元素。
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream 操作是延迟执行的,会等到需要结果的时候才执行。
- 流的基本使用
- 从集合中获取流
- 对流进行筛选、切片、映射、查找、去除重复(中间操作),
- 可以调用多次
- 必须要执行一个终结操作,例如遍历,获取总数等等
- 只能执行一次
流的获取方式
- 通过Collection对象的stream()或parallelStream()方法。
- 通过Arrays类的stream()方法。
通过Stream接口的of()、iterate()、generate()方法。
//自动无限生产数据Stream s2 = Stream.generate(new Supplier<Integer>() {@Overridepublic Integer get() {return new Random().nextInt(100);}});s2.limit(5) //只获取5个数据.forEach(i->{System.out.println(i);});
通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法。
流的中间操作的方法
- filter:过滤器操作
- limit:限制只有几个
- skip:跳过元素
- distinct:去掉重复,对象的话要重写equals方法
- sort:排序
s2.limit(10).sorted((o1, o2) -> o2-o1).forEach(i->{System.out.println(i);});
流的终结操作的方法
- foreach:循环
- max:最大值
- reduce:可以根据自定义规则计算数据
- collect:将筛选之后结果,保存在新的集合中
