1. 方法引用
特殊的lambda表达式,它是对lambda表达式的一种简写方式。
1.1 方法引用的由来
如果我们在Lambda中所指定的功能,已经有其他方法存在相同方案,那是否还有必要再写重复逻辑?可以直接“引 用”过去就好了:---方法引用。::
public class Test {
public static void main(String[] args) {
fun(Test::sum);
}
public static void fun(Consumer<int[]> consumer){
int[] arr = {1,2,3,4,5};
consumer.accept(arr);
}
public static void sum(int[] arr){
int sum = 0;
for (int i : arr) {
sum+=i;
}
System.out.println("结果为:" + sum);
}
}
1.2 方法引用的类型
方法引用是Lambda表达式的一种简写形式。如果Lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用
1.3 静态方法引用
(args)->类名.静态方法(args). 当lambda表达式中方法体,只有一条语句,而这条语句是类名.静态方法。而静态方法的参数和lambda的参数一致时。
类名::静态方法;
1.4 实例方法引用
(args) -> inst.instMethod(args)
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。
首先创建一个Student实体类
public class Student {
private String name;
private Integer age;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public Integer getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
实例引用的案例代码
public class Test01 {
public static void main(String[] args) {
Student s = new Student("邵一凡", 19);
//通过内置函数接口,返回对象的名称
// fun(()->s.getName());
fun(s::getName);//使用实例方法引用
// Function<Student,String> function = (t)->{return t.getName();};//有参有返回值
Function<Student,String> function = Student::getName;//有参有返回值
String apply = function.apply(s);
System.out.println(apply);//这个使用对象方法引用
}
public static void fun(Supplier<String> supplier) {
String s = supplier.get();
System.out.println(s);
}
}
1.5 对象方法引用
lambda: (inst,args)->inst.普通方法(args): -------->类名::普通方法
若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。
public class Test02 {
public static void main(String[] args) {
//判断两个字符串是否相等
//Lambda表达式
fun((s1,s2)->{return s1.equals(s2);});
//使用对象方法引用写
fun(String::equals);
}
public static void fun(BiFunction<String,String,Boolean> booleanBiFunction){
Boolean apply = booleanBiFunction.apply("hello", "hello");
System.out.println(apply);
}
}
1.6 构造方法引用
(args) -> new 类名(args)------构造方法引用: 类名::new
public class Test03 {
public static void main(String[] args) {
// Supplier<Student> supplier =()->new Student();
// Student student = supplier.get();
// System.out.println(student);
Supplier<Student> supplier =Student::new;//使用了构造方法引用
Student student = supplier.get();
System.out.println(student);
BiFunction<String,Integer,Student> biFunction = (s,a)->{return new Student(s,a);};
Student s = biFunction.apply("邵一凡", 19);
System.out.println(s);
}
}
2. Stream
java8中的两个重大改变,一个是Lambda表达式,另一个就是Stream API表达式。 Stream是Java8中处理集合的关键抽象概念,他可以对集合进行非常复杂的查找、过滤、筛选等操作。
2.1 为什么使用stream流
当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。我们来体验 集合操作数据的弊端,需求如下:
一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,成俊杰,张三丰
需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
public class Test {
public static void main(String[] args) {
// 一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,成俊杰,张三丰
// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
List<String> list=new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("程俊杰");
list.add("张三丰");
//1.拿到所有姓张的
List<String> newList01=new ArrayList<>();
for(String n:list){
if(n.startsWith("张")){
newList01.add(n);
}
}
//2.拿到名字长度为3个字的
List<String> newList02=new ArrayList<>();
for(String n:newList01){
if(n.length()==3){
newList02.add(n);
}
}
//3.打印这些数据
for(String s:newList02){
System.out.println(s);
}
}
}
分析:
循环遍历的弊端
这段代码中含有三个循环,每一个作用不同:
首先筛选所有姓张的人;
然后筛选名字有三个字的人;
最后进行对结果进行打印输出。
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环 是做事情的方式,而不是目的。每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,只能再使 用另一个循环从头开始。
那Stream能给我们带来怎样更加优雅的写法呢?
Stream初体验
public class Test01 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张三");
list.add("程俊杰");
list.add("张三丰");
list.stream().filter(t->t.startsWith("张")).filter(t->t.length()==3).forEach(System.out::println);
}
}
2.2 Stream流的原理
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工 处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
2.3 步骤
(1)获取Stream流对象
(2) 中间操作---返回类型还是Stream流对象。
(3)终止操作---不在是Stream流对象
2.4 获取Stream流对象的方式
(1) 通过集合对象调用stream()
(2)通过Arrays获取stream流对象
(3)通过Stream流里面of方法
public class Test02 {
public static void main(String[] args) {
//通过集合对象调用stream()
ArrayList<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.stream().forEach(System.out::println);
//通过使用Arrays工具类
String[] arr = {};
Stream<String> stream = Arrays.stream(arr);
//通过Stream类
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
//以上的都是串行流
System.out.println("~~~~~~~~~~~~~~~~~~~");
Stream<String> stringStream = list.parallelStream();//并行流
stringStream.forEach(System.out::println);
}
}
2.5 Stream流的api方法
举个简单的例子:
假设有一个Person类和一个Person列表,现在有两个需求:1)找到年龄大于18岁的人并输出;2)找出所有中国人的数量。
@Data
class Person {
private String name;
private Integer age;
private String country;
private char sex;
public Person(String name, Integer age, String country, char sex) {
this.name = name;
this.age = age;
this.country = country;
this.sex = sex;
}
}
public class Test {
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'));
//1. 年龄大于18 filter:过滤掉不满足条件的元素. forEach:输出元素. ---如果没有终止函数,那么中间函数的代码不会被执行。
personList.stream(). filter(item->{
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~");
return item.getAge()>18;
}).forEach(System.out::println);
//2. 找出中国人 并统计个数: count()
long count = personList.stream().filter(item -> item.getCountry().equals("中国")).count();
System.out.println("中国人:"+count);
}
}
(2)找出年龄最大和最小
Person person = personList.stream().max((o1, o2) -> o1.getAge() - o2.getAge()).get();
Person person1 = personList.stream().min((o1, o2) -> o1.getAge() - o2.getAge()).get();
System.out.println("年龄最大的:" + person);
System.out.println("年龄最小的:" + person1);
(3)map-->
会把集合中的元素转化成另一种类型
//map的使用
//personList.stream().filter(item->item.getCountry().equals("中国")).map(item->item.getName()).forEach(System.out::println);
personList.stream().filter(item->item.getCountry().equals("中国")).map(item->new P(item.getName(), item.getAge())).forEach(System.out::println);
public class Test04 {
public static void main(String[] args) {
//整数数组每个元素+3
List<Integer> list = Arrays.asList(1, 17, 27, 7);
list.stream().map(item->item+3).forEach(System.out::println);
List<String> list2=Arrays.asList("hello","world","java","spring","springmvc");
//字符串大写
list2.stream().map(String::toUpperCase).forEach(System.out::println);
}
}
(4)收集 collect
把处理过的集合搜集成新的集合。
List<Person> personList = new ArrayList<>();
personList.add(new Person("小梅",24,"中国",'F'));
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("Tom",21,"中国",'F'));
personList.add(new Person("李康",22,"中国",'M'));
//把Person-年龄大于20人--里面名称----新的集合。
List<String> collect = personList.stream().filter(item -> item.getAge() > 20).map(item -> item.getName()).collect(Collectors.toList());
System.out.println(collect);
(5)sorted排序
//sorted排序
List<Person> collect1 = personList.stream().sorted(((o1, o2) -> o1.getAge() - o2.getAge())).collect(Collectors.toList());
System.out.println(collect1);
(6) reduce规约
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
整型集合: -----请求。[1,2,3,4,5]=
public class Test05 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Integer integer = list.stream().reduce((t1, t2) -> t1 + t2).get();
System.out.println(integer);
}
}
(7)查询第一个findFirst
//查询第一个findFirst
Optional<Person> first = personList.stream().filter(item -> item.getAge() >= 18 && item.getAge() <= 20).findFirst();
System.out.println(first.get());
(9)去重、合并(distinct、skip、limit)
流也可以进行合并、去重、限制、跳过等操作。
public class Test06 {
public static void main(String[] args) {
String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "d", "e", "f", "g" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
//去重操作
/*List<String> collect = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
System.out.println(collect);*/
//限制
/*List<String> collect = Stream.concat(stream1, stream2).limit(3).collect(Collectors.toList());
System.out.println(collect);*/
//跳过
List<String> collect = Stream.concat(stream1, stream2).distinct().skip(3).collect(Collectors.toList());
System.out.println(collect);
}
}
(10)匹配 anyMatch() allMatch() noMatch()
//匹配
List<String> collect = Stream.concat(stream1, stream2).collect(Collectors.toList());
boolean d = collect.stream().anyMatch(item -> item.equals("a"));
System.out.println(d);