13.1 Lambda表达式
-
举例:(o1,o2)->Integer.compare(o1,o2)
-
格式:
->:Lambda操作符 或 箭头操作符
->左边:Lambda形参列表(其实就是接口中的抽象方法的形参列表)
->右边:Lambda体(其实就是重写的抽象方法的方法体
-
Lambda表达式的使用 :(6种情况)
-
Lambda表达式的本质:作为函数式接口的实例,是一个匿名实现类的对象
-
只包含一个抽象方法的接口称为函数式接口,可以加注解@FunctionalInterface进行标注校验
总结:
- 左边:
- 形参列表的参数类型可以省略(类型推断)
- 如果形参列表只有一个参数,小括号可以省略
- 右边:如果Lambda体只有一条执行语句(可以是return语句),可以省略大括号{}以及return关键字
语法格式一:无参、无返回值
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("helloworld");
}
};
Runnable r1 = ()->{System.out.println("helloworld");};
语法格式二:需要一个参数,但是没有返回值
Consumer<String> con = new Comsumer<String>(){
@Override
public void accept(Stirng s){
System.out.println(s);
}
};
Comsumer<String> con = (String s)->{System.out.println(s);};
语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
Comsumer<String> con = (s)->{System.out.println(s);};
语法格式四:Lambda若只需要一个参数,参数的小括号可以省略
Comsumer<String> con = s->{System.out.println(s);};
语法格式五:Lambda需要多个参数,多条执行语句,并且有返回值
Comparator<Integer> com = (o1,o2) -> {
sout(o1);
sout(o2);
return o1.compareTo(o2);
}
语法格式六:当Lambda体只有一条语句时,return与大括号都可以省略
Comparator<Integer> com = (o1,o2)->o1.compareTo(o2);
方法引用
Comparator<Integer> comparator2 = Integer :: compare;
System.out.println(comparator2.compare(10,20));
13.2 函数式接口
- 消费型接口 Comsumer< T > void accept(T t)
- 供给型接口 Suppelier< T > T get()
- 函数型接口 Function< T,R > R apply(T t)
- 断定型接口 Perdicate< T > boolean test(T t)
消费型接口举例:
public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}
@Test
public void test1(){
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("消费了" + aDouble);
}
});
// Lambda写法
happyTime(600, o-> System.out.println("lambda消费了"+o));
}
供给型接口举例:
// 根据给定的规则,规律集合中的字符串,此规则由Predicate的方法决定
public List<String > filterString(List<String> list, Predicate<String> pre){
List<String> filterList = new ArrayList<>();
for(String s : list){
if(pre.test(s)){
filterList.add(s);
}
}
return filterList;
}
@Test
public void test2(){
List<String> list = Arrays.asList("北京","东京","南京","天津","西京");
System.out.println(filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
}));
// Lambda写法
System.out.println(filterString(list,s -> s.contains("京") ));
}
方法引用
- 使用情景:当要传递给Lambda体的操作已经有实现的方法了。可以使用方法引用
- 方法引用本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例,所以方法引用也是函数式接口的实例
- 使用格式: 类(或对象) : : 方法名
- 主要有以下三种情况
- 对象 : : 非静态方法
- 类 : : 静态方法
- 类 : : 非静态方法
- 要求:接口中的抽象方法与方法引用的方法的形参列表和返回值类型相同(针对于情况1、2)
情况一:对象 : : 实例方法
//抽象方法:Consumer中的void accept(T t)
//引用方法:PrintStream中的void println(T t)
Consumer<String> consumer = o-> System.out.println(o);
consumer.accept("北京");
PrintStream ps = System.out;
Consumer<String> consumer1 = ps :: println;
consumer1.accept("Beijing");
情况二:类 : : 静态方法
//抽象方法:Comparator中的int compare(T t1, T t2)
//引用方法:Integer中的int compare(T t1, T t2)
Comparator<Integer> com = (o1,o2)-> Integer.compare(o1,o2);
System.out.println(com.compare(12,21));
Comparator<Integer> com1 = Integer::compareTo;
System.out.println(com1.compare(12,21));
情况三 :类 : : 实例方法
//抽象方法:Comparator中的int compare(T t1, T t2)
//引用方法:String中的int t1.compareTo(t2)
Comparator<String> com1 = (s1,s2)->s1.compareTo(s2);
System.out.println(com1.compare("abc","abf"));
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("abc","abg"));
//抽象方法:BiPredicate中的boolean test(T t1, T t2)
//引用方法:String中的int t1.equals(t2)
BiPredicate<String,String> pre1 = (s1,s2)->s1.equals(s2);
System.out.println(pre1.test("abc","abc"));
BiPredicate<String,String> pre2 = String::equals;
System.out.println(pre2.test("abc","abc"));
构造器引用
与方法引用类似,函数式接口的抽象方法和构造器的形参列表一致,抽象方法的返回值类型即为构造器所属的类的类型
//Supplier中的 T get()
Supplier<Object> sup = new Supplier<Object>() {
@Override
public Object get() {
return new Object();
}
};
System.out.println(sup.get());
Supplier<Object> sup1 = ()->new Object();
System.out.println(sup1.get());
Supplier<Object> sup2 = Object :: new;
System.out.println(sup2.get());
//Function中的R apply(T t)
Function<String , String> func1 = new Function<String, String>() {
@Override
public String apply(String s) {
return new String(s);
}
};
System.out.println(func1.apply("abc"));
Function<String , String> func2 = (o)->new String(o);
System.out.println(func2.apply("bcd"));
Function<String,String> func3 = String :: new;
System.out.println(func3.apply("poi"));
数组引用
可以把数组看做是一个特殊的类,则写法与构造器引用类似
Function<Integer,String[]> func1 = new Function<Integer, String[]>() {
@Override
public String[] apply(Integer integer) {
return new String[integer];
}
};
System.out.println(Arrays.toString(func1.apply(5)));
Function<Integer,String[]> func2 = len->new String[len];
System.out.println(Arrays.toString(func2.apply(6)));
Function<Integer,String[]> func3 = String[] :: new;
System.out.println(Arrays.toString(func3.apply(7)));
13.3 Stream API
概述
Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
-
Stream关注的是对数据的运算,与cpu相关
集合关注的是数据的存储,与内存相关
-
注意点:
- Stream不会存储元素
- Stream不会改变源对象,相反,会返回一个持有结果的新Stream
- Stream操作是延迟执行的,会等到需要结果的时候才执行
-
Stream执行流程
- Stream实例化
- 一些列的中间操作(过滤、映射…):一个中间操作链,对数据源的数据进行处理
- 终止操作:一旦执行终止操作,就执行中间操作链,并产生结果。之后不会再被使用
本节测试用到的Employee类
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public static List<Employee> getEmployess(){
List<Employee> list = new ArrayList<>();
list.add(new Employee(1001,"马化腾",24,2000));
list.add(new Employee(1002,"马云",64,3000));
list.add(new Employee(1003,"刘强东",18,4000));
list.add(new Employee(1004,"雷军",24,5000));
list.add(new Employee(1005,"李彦宏",36,6000));
list.add(new Employee(1006,"比尔盖茨",31,7000));
list.add(new Employee(1007,"任正非",34,8000));
list.add(new Employee(1008,"扎克伯格",66,9000));
return list;
}
public Employee() {
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
if (id != employee.id) return false;
if (age != employee.age) return false;
if (Double.compare(employee.salary, salary) != 0) return false;
return name != null ? name.equals(employee.name) : employee.name == null;
}
@Override
public int hashCode() {
int result;
long temp;
result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
temp = Double.doubleToLongBits(salary);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
实例化
创建Stream方式一:通过集合
List<Employee> employess = Employee.getEmployess();
// default Stream<E> stream() : 返回一个顺序流
Stream<Employee> stream = employess.stream();
// default Stream<E> parallelStream():返回一个并行流
Stream<Employee> parallelStream = employess.parallelStream();
创建Stream方式二:通过数组
// 调用Arrays类的静态方法 static <T> Stream<T> stream(T[] array):返回一个流
int[] arr = new int[]{1,2,3,4,5,6};
IntStream stream1 = Arrays.stream(arr);
Employee e1 = new Employee(1001, "tom", 23, 6000);
Employee e2 = new Employee(1002, "jer", 22, 5000);
Employee[] arr1 = new Employee[]{e1,e2};
Stream<Employee> stream2 = Arrays.stream(arr1);
创建Stream方式三:通过Stream的of()
Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5, 6);
创建Stream方式四:创建无限流
//迭代
//public static<T> Stream<T> iterate(final T seed,final UnaryOperator<T> f)
Stream.iterate(0,i->i+2).limit(10).forEach(System.out::println);
// 生成
//public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);
中间操作
切片与筛选、映射、排序
1、筛选与切片
-
filter(Predicate p)——接收Lambda,从流中排除某些元素
练习:查询员工表中工资大于5000的并输出
Stream<Employee> stream = list.stream();
/*stream.filter(new Predicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary()>=5000;
}
});*/
stream.filter(e -> e.getSalary() > 5000).forEach(System.out::println);
-
limit(int n)——截断流,使其元素不超过给定数量n
list.stream().limit(3).forEach(System.out::println);
-
skip(int n)——跳过元素,返回一个跳过前n个元素的流,若不足n个则返回空流
list.stream().skip(3).forEach(System.out::println);
-
distinct()——筛选,通过流所生成元素的hashCode()和equals()去除重复元素
2、映射
-
map(Function func)——接受一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素
List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); /* list.stream().map(new Function<String, Object>() { @Override public Object apply(String s) { return s.toUpperCase(); } }).forEach(System.out::println);*/ list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
练习:输入员工姓名长度大于3的姓名
List<Employee> list = Employee.getEmployess(); Stream<String> nameStream = list.stream().map(Employee::getName); nameStream.filter(e->e.length()>3).forEach(System.out::println);
-
flatMap(Function fun)——接受一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流,类似List a.addAll(List b)
//将字符串转换成字符流 public static Stream<Character> strToStream(String str){ ArrayList<Character> list = new ArrayList<>(); for(char c : str.toCharArray()){ list.add(c); } return list.stream(); } @Test public void test4(){ List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); list.stream().map(StreamTest1::strToStream).forEach( s -> s.forEach(System.out::println) ); list.stream().flatMap(StreamTest1::strToStream).forEach(System.out::println); }
3、排序
List<Integer> list = Arrays.asList(1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
-
sorted()——自然排序
list.stream().sorted().forEach( System.out::println);
-
sorted(Comparator com)——定制排序
list.stream().sorted(((o1, o2) -> o2-o1)).forEach(System.out::println);
终止操作
匹配与查找、规约、收集
1、匹配与查找
List<Employee> list = Employee.getEmployess();
-
allMatch(Predicate p)——检查p是否匹配所有元素
boolean allMatch = list.stream().allMatch(e -> e.getAge() > 18);
-
anyMath(Predicate p)——检查p是否匹配至少一个元素
boolean anyMatch = list.stream().anyMatch(e -> e.getAge() > 18);
-
noneMath(Predicate p)——检查是否没有p匹配的元素
boolean noneMatch = list.stream().noneMatch(e -> e.getName().startsWith("雷"));
-
findFirst()——返回第一个元素
Optional<Employee> first = list.stream().findFirst();
-
findAny()——返回当前流中的任意元素
Optional<Employee> any = list.parallelStream().findAny();
-
count()——返回流中元素的总个数
long count = list.stream().filter(e -> e.getSalary() > 5000).count();
-
max(Comparator c)——返回流中最大值
Stream<Double> salaryStream = list.stream().map(e -> e.getSalary()); Optional<Double> max = salaryStream.max(Double::compare);
-
min(Comparator c)——返回流中最小值
Optional<Employee> min = list.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
-
forEach(Consumer c)——内部迭代
list.stream().forEach(System.out::println); //使用集合的遍历操作 list.forEach(System.out::println);
2、规约
- reduce(T identity, BinaryOperator)——将流中元素反复结合,得到一个值,返回这个值
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional<Integer> reduce = list.stream().reduce(0, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
});
//方法引用
Optional<Integer> reduce1 = list.stream().reduce(0, Integer::sum);
- reduce(BinaryOperator)——将流中元素反复结合,得到一个值,返回Optional< T >
List<Employee> list = Employee.getEmployess();
Optional<Double> totalSalary = list.stream().map(Employee::getSalary).reduce(Double::sum);
3、收集
collect(Collector c):将流转换为其他形式,接受一个Collector接口的实现,用于给Stream中元素做汇总
使用Collectors的方法可以获取Collector接口的实现
List<Employee> list = Employee.getEmployess();
List<Employee> employeeList = list.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);
13.4 Optional类
Optional< T >类(java.util.Optional)是一个容器类,可以保存类型T的值,代表这个值存在;或者仅仅保存null,表示这个值不存在
原来用null表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异
常用方法:ofNullable(T t)
orElse(T t)
Option类提供很多方法,这样就不用显式进行空值检测
我的学习笔记有更多精彩内容哦
Java编程知识专栏