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() {
@Override
public void run() {
System.out.println("我是匿名内部类");
}
};
myInferface1.run();
/////省略引用
new MyInferface1(){
@Override
public 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,可以检测当前接口是否是函数接口
@FunctionalInterface
public 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>() {
@Override
public 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>() {//过滤方法
@Override
public boolean test(String o) {
if(o.contains("张")){
return true;
}
return false;
}
}).filter(new Predicate<String>() {//筛选
@Override
public 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>() {
@Override
public 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:将筛选之后结果,保存在新的集合中