1. lambda表达式
接口实现
第一种方式:创建实现类 ,用实现类进行接口的实现
第二种方式:通过匿名内部类进行进行实现
1.1 内部类 与 lambda表达式的区别
基于匿名内部类实现的方式,特点:
内部类:
1 代码比较复杂,比较繁琐
2 匿名内部类,类,执行过程中,需要进行编译,------》花费时间比较长
3 需要编译,形成class文件,在硬盘上占用空间
4匿名内部类 实现接口,接口中可以有多个抽象方法
lambda表达式:
1 代码简化,不形成类,不需要编译,不需要占用硬盘空间(不生成.class文件)
2 lambda表达式,只能有一个抽象方法
1.2 lambda表达式使用
lambda表达式:
格式:
实现的接口中方法的形参 指向 方法体(方法逻辑)
() -> {}
对lambda表达式简化
格式:()->{}
1 参数部分的简化:
如果方法是无参方法,()不能省略
如果方法是有参数的方法,参数数据类型可以省略
注意:如果参数有多个,数据类型同时省略
注意:如果参数有一个,数据类型可以省略,并且在数据类型省略的同时,()可以省略
2 方法的方法体简化
有返回值的方法,无返回值方法
无返回值方法:如果方法中只有一句方法的逻辑代码,{}可以省略
有返回值的方法:如果方法只有一句代码,省略return关键词,
在return省略的前提下,{}可以省略的
1.2.1 对接口抽象方法实现使用
-
无参数列表,无返回值
public interface Cat { void eat(); } --------------> public class Test1 { public static void main(String[] args) { /** * 方式1:无参数列表,无返回值 */ Cat cat = () -> { System.out.println("我是eat实现"); }; cat.eat(); /** * 简单写法: * 如果方法体中只有一句代码,可以省略{} * 注意:如果没有参数列表,不能省略() */ Cat cat1 = () -> System.out.println("我是eat实现"); cat1.eat(); }
/**
* 简单写法:
* 如果方法体中只有一句代码,可以省略{}
* 注意:如果没有参数列表,不能省略()
*/
Cat cat1 = () -> System.out.println("我是eat实现");
cat1.eat(); -
参数列表,无返回值
/** * 参数列表,无返回值 */ Person p = (String name, int age) -> { System.out.println("名字是:"+name + "年龄是:" + age); }; p.call("张三", 12); /** * 简单写法: * 如果有多个参数,可以省略参数数据类型; * 如果方法体中只有一句代码,可以省略{}; */ Person p1 = (name, age) ->{ System.out.println("名字是:"+name + "年龄是:" + age); }; p1.call("张三", 12); //最终写法 Person p2 = (name, age) -> System.out.println("名字是:"+name + "年龄是:" + age); p2.call("张三", 12);
如果有多个参数,可以省略参数数据类型;
如果方法体中只有一句代码,可以省略{}; -
有参数列表,有返回值
/** * 有参数列表,有返回值 */ shape shape1 = (String b)->{ return "面积为" +b; }; String area = shape1.area("12平方公里"); System.out.println(area); /** * 简单写法: * 如果只有一个参数,可以省略参数数据类型,并且可以省略参数列表括号; * 如果方法体只有一句代码,可以省略{},如果含有return,也可以省略retren */ shape shape2 = b -> { return "面积为" +b; }; shape2.area("12平方公里"); //最终写法 shape shape3 = b -> "面积为"+b; shape2.area("12平方公里");
如果只有一个参数,可以省略参数数据类型,并且可以省略参数列表括号;
如果方法体只有一句代码,可以省略{},如果含有return,也可以省略retren
1.2.2 方法的引用
- 静态方法的引用
在lambda表达式中,进行方法引用,逻辑实现中的方法的参数和接口中要实现的方法的参数保持一致
对静态方法的引用:
格式:类名::方法名
public interface Sum {
/**
* 接口抽象方法
* @param a
* @param b
* @return
*/
int add(int a, int b);
}
------------------------------->
public class SumUtil {
/**
* 静态方法
* @param a
* @param b
* @return
*/
public static int addMethod(int a, int b){
return a + b;
}
}
------------------------------------------>
/**
* 静态方法调用
*/
Sum sum1 = (a, b) -> SumUtil.addMethod(a,b);
System.out.println("计算结果为:" + sum1.add(10,20));
//简化写法
Sum sum = SumUtil::addMethod;
System.out.println("计算结果为:" + sum.add(10,20));
- 实例方法的引用
lambda表达式对实例方法的引用:
格式:new 构造方法 ::实例方法名
对象::实例方法名
public interface Change {
/**
* 接口抽象方法
* @param s
* @return
*/
String toUpper(String s);
}
-------------------------------------->
public class ChangeUtil {
/**
* 自己写的将小写转换成大写
* @param str
* @return
*/
public String upper(String str){
String result = "";
byte[] chars = str.getBytes();
for (byte c:chars) {
result += (char)(c-32);
}
return result;
}
}
-------------------------------------->
/**
* 实例方法调用
*/
Change change1 = s -> new ChangeUtil().upper(s);
System.out.println(change1.toUpper("hello"));
//简化写法
Change change = new ChangeUtil()::upper;
System.out.println(change.toUpper("hello"));
- 类对实例方法引用
方法的引用
类对实例方法引用
类名::实例方法
/**
* Dog类
*/
public class Dog {
private String name;
private Integer age;
private String type;
public Dog(String name, Integer age, String type) {
this.name = name;
this.age = age;
this.type = type;
}
public Dog() {
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", type='" + type + '\'' +
'}';
}
}
------------------------------------------------>
/**
* 创建Dog抽象类
*/
public interface CreateDog {
//创建Dog对象
Dog creDog();
}
------------------------------------------------->
/**
* 构造方法引用
*/
CreateDog cc = () -> new Dog();
System.out.println(cc.creDog());
//简化写法
CreateDog cc1 = Dog :: new;
System.out.println(cc1.creDog());
- 数组方法引用
/**
* 创建数组抽象类
*/
public interface CreateArray {
int[] create(int length);
}
--------------------------------->
/**
* 数组方法引用
*/
CreateArray ca = (int length) -> new int[length];
System.out.println(ca.create(5));
//简化写法
CreateArray ca1 = int[]::new;
System.out.println(ca1.create(5));
2.常用接口
java8 新特性
接口中默认是抽象方法和常量
接口中可以出现方法体
1 方法有方法体,需要使用default关键词进行修饰
default void a(){
接口中default修饰的方法,可以有方法体,并且实现类可以有选择性的重写
}
2 接口中可以有静态方法
static void a(){
}
2.1 Predicate (条件判断的接口)
import java.util.function.Predicate;
public class Test02 {
public static void main(String[] args) {
/**
* Predicate 条件判断的接口
*/
Predicate<Integer> pre = new Predicate<Integer>(){
@Override
public boolean test(Integer integer) {
return integer > 100;
}
};
System.out.println(pre.test(150));
/**
* 使用lambda表达式
*/
Predicate<Integer> pre1 = integer -> integer > 100;
System.out.println(pre1.test(150));
/**
* 并且条件判断
*/
Predicate<Integer> pre2 = integer -> integer > 10;
Predicate<Integer> pre3 = integer -> integer > 90;
//50是否大于10 并且 50是否大于90
/**
* 我们lambda表达式只适合含有一个抽象方法的类,我们这边发现还可以掉and方法,是不是有问题呢,不是的,and是default修饰的方法不是抽象方法
*/
System.out.println(pre2.and(pre3).test(50));
/**
* 或者条件判断
*/
Predicate<Integer> pre4 = integer -> integer > 10;
Predicate<Integer> pre5 = integer -> integer > 90;
//50是否大于10 或者 50是否大于90
System.out.println(pre4.or(pre5).test(50));
/**
* 取反
*/
Predicate<Integer> pre6 = integer -> integer > 10;
//20是否大于10,在进行取反
System.out.println(pre6.negate().test(20));
}
}
2.2 Comparator(比较接口)
/**
* Comparator 比较接口
* o1 - o2 升序
* o2 - o1 降序
*/
TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
TreeSet<Integer> set1 = new TreeSet<>( (o1,o2) -> o1-o2);
set1.add(20);
set1.add(10);
set1.add(35);
set1.add(30);
System.out.println(set1);
2.3 Function(类型转换接口)
第一个泛型类型的数据转成第二个泛型类型的功能
/**
* Function 类型转换接口
*/
Function<String, Integer> fun = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
};
Integer apply = fun.apply("123");
System.out.println(apply instanceof Integer); // true
// Function<String, Integer> fun1 = (String s) -> {return Integer.parseInt(s);};
// Function<String, Integer> fun1 = s -> Integer.parseInt(s);
Function<String, Integer> fun1 = Integer::parseInt;
Integer apply1 = fun1.apply("234");
System.out.println(apply1 instanceof Integer); // true
3 lambda表达式+接口应用场景
3.1 stream(集合中的流)
Java8 中添加了一个新的接口类 Stream,相当于高级版的 Iterator,它可以通过 Lambda 表达式对集合进行大批量数据操作,或 者各种非常便利、高效的聚合数据操作。
在 Java8 之前,我们通常是通过 for 循环或者 Iterator 迭代来重新排序合并数据,又或者通过重新定义 Collections.sorts 的 Comparator 方法来实现,这两种方式对于大数据量系统来说,效率并不是很理想。Stream 的聚合操作与数据库 SQL 的聚合操作 sorted、filter、map 等类似。我们在应用层就可以高效地实现类似数据库 SQL 的 聚合操作了,而在数据操作方面,Stream 不仅可以通过串行的方式实现数据操作,还可以通过并行的方式处理大批量数据,提高数据 的处理效率。
水开始流的位置:开始流
水流流过的位置:中间流
水流到山脚下结束:结束流
注意:流都是一次性
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(100);
list.add(25);
list.add(63);
list.add(5);
list.add(105);
list.add(98);
list.add(69);
list.add(25);
}
- filter 判断数据是否满足条件
/**
* 1. 获取stream流的方法 ------> 开始流的方法
*/
Stream<Integer> stream = list.stream();
/**
* 2. 对流进行操作的方法 =========> 中间流的方法
*/
/**
* 2.1 filter 判断数据是否满足条件
*/
stream.filter(integer -> integer>50 ).forEach(System.out::println);
- toArray() 将数据保存到数组中
/**
* 2.2 .toArray() 将数据保存到数组中
*/
Object[] objects = stream.filter(integer -> integer > 50).toArray();
System.out.println(Arrays.toString(objects)); //[100, 63, 105, 98, 69]
- distinct 去重
/**
* distinct 数据去重
*/
Object[] objects = stream.distinct().toArray();
System.out.println(Arrays.toString(objects));
- sorted 排序,默认降序
/**
* sorted 排序 默认升序排列
*/
Object[] objects = stream.sorted().toArray();
System.out.println(Arrays.toString(objects));
自己指定排序条件
/**
* 自定义顺序,自定义降序
*/
Object[] objects = stream.sorted((o1, o2) -> o2 - o1).toArray();
System.out.println(Arrays.toString(objects));
- 中间流进行多次操作,排序 去重 指定条件的数据的显示
/**
* 指定条件的数据的显示
* 去重后升序过滤出大于25的数据
*/
Object[] objects = stream.distinct().sorted().filter(integer -> integer >25).toArray();
System.out.println(Arrays.toString(objects)); //[63, 69, 98, 100, 105]
- 数据 指定条数数据的展示
/**
* limit 数据指定条数的数据进行展示
* 去重后升序过滤出前三条数据
*/
Object[] objects = stream.distinct().sorted().limit(3).toArray();
System.out.println(Arrays.toString(objects));
- max() 最大值、min() 最小值
/**
* max() 最大值、min() 最小值
*/
// Integer integer = stream.distinct().max((o1, o2) -> o1 - o2).get();
// System.out.println(integer); //105
Integer integer = stream.distinct().min((o1, o2) -> o1 - o2).get();
System.out.println(integer); //5
- map 转成其他数据类型
/**
* map 转成其他数据类型
*/
//将所有数据都拼接abc
Object[] objects = stream.distinct().map(s -> s + "abc").toArray();
System.out.println(Arrays.toString(objects)); //[100abc, 25abc, 63abc, 5abc, 105abc, 98abc, 69abc]
doubleValue(全部转换成double类型),longValue(全部转换成long类型),intValue(全部转换成int类型)
/**
* 只进行某一个类型转换
* doubleValue(全部转换成double类型),longValue(全部转换成long类型),intValue(全部转换成int类型)
*/
// double[] doubles = stream.mapToDouble(Integer::doubleValue).toArray();
// System.out.println(Arrays.toString(doubles)); //[100.0, 25.0, 63.0, 5.0, 105.0, 98.0, 69.0, 25.0]
// long[] longs = stream.mapToLong(Integer::longValue).toArray();
// System.out.println(Arrays.toString(longs)); //[100, 25, 63, 5, 105, 98, 69, 25]
int[] ints = stream.mapToInt(Integer::intValue).toArray();
System.out.println(Arrays.toString(ints)); //[100, 25, 63, 5, 105, 98, 69, 25]
-
reduce 数据运算
/** * reduce 数据运算 */ //将数组中所有数据相加 Integer integer = stream.reduce((integer1, integer2) -> integer1 + integer2).get(); System.out.println(integer);
-
对流中数据进行判断,是否所有的都满足条件 (allMatch)、对流中数据进行判断,是否任意一条数据的都满足条件(anyMatch)、 据进行判断,是否所有数据的都不满足条件 (noneMatch)
/**
* 对流中数据进行判断,是否所有的都满足条件(allMatch)、
* 对流中数据进行判断,是否任意一条数据的都满足条件(anyMatch)、
* 据进行判断,是否所有数据的都不满足条件(noneMatch)
*/
// boolean b = stream.allMatch(integer -> integer > 26);
// System.out.println(b); // false
// boolean b = stream.anyMatch(integer -> integer > 26);
// System.out.println(b); // true
boolean b = stream.noneMatch(integer -> integer > 26);
System.out.println(b); // false
- count()数据个数的统计
/**
* count()数据个数的统计
*/
long count = stream.distinct().count();
System.out.println(count);
3.2、stream流Collectors.toMap用法
3.2.1、使用规则
toMap(Function, Function) 返回一个 Collector,它将元素累积到一个 Map中,其键和值是将提供的映射函数应用于输入元素的结果。
如果映射的键包含重复项,则在执行收集操作时会抛出IllegalStateException。如果映射的键可能有重复项,请改用 toMap(Function, Function, BinaryOperator)。
3.2.2、代码演示
1、创建一个实体类
public class Person {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(Integer id, String name) {
this.id = id;
this.name = name;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
2、收集list中的peson对象,将数据转换成 {id:person对象}的形式
public static void main(String[] args) {
List<Person> list = new ArrayList();
list.add(new Person(1, "1"));
//list.add(new Person(1, "4"));
list.add(new Person(2, "2"));
list.add(new Person(3, "3"));
//收集list中的peson对象,将数据转换成 {id:person对象}的形式。Person::getId与 e->e.getId()一样
Map<Integer, Person> collect = list.stream().collect(Collectors.toMap(Person::getId, i -> i));
System.out.println(collect);
}
使用toMap()函数之后,返回的就是一个Map了,自然会需要key和value。
toMap()函数:参数1:就是用来生成key值的,参数2:用来生成value值的
参数3:用在key值冲突的情况下,如果新元素产生的key在Map中已经出现过了,第三个参数就会定义解决的办法。
我们看到上面的list中person对象的id都是不同的,因此不需要第三个参数。
如果我们list中存储相同id的person对象
public static void main(String[] args) {
List<Person> list = new ArrayList();
list.add(new Person(1, "1"));
list.add(new Person(1, "4"));
list.add(new Person(2, "2"));
list.add(new Person(3, "3"));
//收集list中的peson对象,将数据转换成 {id:person对象}的形式
Map<Integer, Person> collect = list.stream().collect(Collectors.toMap(Person::getId, i -> i));
System.out.println(collect);
}
运行报错,因为map集合key唯一
解决办法,我们就需要使用第三个参数了,(a,b)->a :表示使用第一次出现的,(a,b)->b:表示使用最后一次出现的
public static void main(String[] args) {
List<Person> list = new ArrayList();
list.add(new Person(1, "1"));
list.add(new Person(1, "4"));
list.add(new Person(2, "2"));
list.add(new Person(3, "3"));
//(a,b)->b 表示如果list中存在id相同的person对象,后面的person对象覆盖前面的map中已存在的。
Map<Integer, Person> collect2 = list.stream().collect(Collectors.toMap(Person::getId, v -> v, (a,b)->b));
System.out.println("collect2" + collect2);
//(a,b)->b 表示如果list中存在id相同的person对象,后面的person对象不覆盖前面的map中已存在的,因此使用第一次出现的person对象。
Map<Integer, Person> collect3 = list.stream().collect(Collectors.toMap(Person::getId, v -> v, (a,b)->a));
System.out.println("collect3" + collect3);
}
3、Function.identity()
偶然之间发现的这个函数,感觉还是很有用的,尤其实在返回map的时候,value还为本身,用起来就很方便。
Java 8允许在接口中加入具体方法。接口中的具体方法有两种,default方法和static方法,identity()就是Function接口的一个静态方法。
Function.identity()返回一个输出跟输入一样的Lambda表达式对象,等价于形如t -> t形式的Lambda表达式,底层就是t->t
实例代码
public static void main(String[] args) {
List<Person> list = new ArrayList();
list.add(new Person(1, "1"));
list.add(new Person(1, "4"));
list.add(new Person(2, "2"));
list.add(new Person(3, "3"));
//第二个参数Function.identity(),底层就是t -> t,就是将当前对象返回
Map<Integer, Person> collect3 = list.stream().collect(Collectors.toMap(e -> e.getId(), Function.identity(), (a, b) -> a));
System.out.println("collect3"+collect3);
//.values(),获取Map集合中所有的value值,放到collection集合中
Collection<Person> values = list.stream().collect(Collectors.toMap(Person::getId, Function.identity(), (a, b) -> a)).values();
System.out.println(values);
}
4. 可变长参数
方法定义时,可以给方法进行参数个数的不固定个数的指定
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
System.out.println(list);
}
我们从上面的例子可以看出,可变长参数,可以传到多个参数,如果我们不清楚传递多个少个参数,我们可以使用可变长参数。
注意:
1. 一个方法中只能有一个可变长参数,并且可变长参数必须在参数列表最后位置 ;
2. 可变长参数,传入到方法中,它是将所有参数都放到数组中,因此我们方法获取到的可变长参数是一个数组,数组里面存放的我们传入的数据;
/**
* 可变长参数
*/
public class demo02 {
/**
* Arrays.asList
*/
public static void main(String[] args) {
me(1,2,3,4,5);
me("a","b","c","d");
me(new Student("zs",12), new Student("ls",36), new Student("ww",43));
}
/**
* 可变长参数
* 格式:T... T(泛型)
* @param arr
* @param <T>
*/
public static <T> void me(T... arr){
System.out.println(Arrays.toString(arr));
}
}