java基础之lambda表达式
1 什么是lambda表达式
lambda表达式是一个匿名函数,允许将一个函数作为另外一个函数的参数,将函数作为参数传递(可理解为一段传递的代码)。
2 为什么要用lambda表达式
lambda表达式可以写出更简洁更灵活的代码。先看看下面一段代码
public class TestLambda {
//匿名内部类方式
@Test
public void test() {
TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
});
}
//lambda方式
@Test
public void test1() {
Comparator<Integer> com = (x,y)->Integer.compare(x, y);
TreeSet<Integer> set = new TreeSet<>(com);
}
List<Person> list = Arrays.asList(new Person("小红", 18, 5000.0), new Person("小明", 21, 6000.0),new Person("小刚", 25, 7000.0));
// 传统方式获取年龄大于20的人
@Test
public void test2() {
for (Person person : list) {
if(person.getAge()>20) {
System.out.println(person);
}
}
}
// lambda方式获取年龄大于20的人
@Test
public void test3() {
list.stream()
.filter((x)-> x.getAge()>20)
.forEach(System.out::println);
}
}
3 lambda语法
java8中引入了操作符“->”,称为箭头操作符或lambda操作符,lambda用于替换匿名函数。
使用lambda表达式的前提:接口(函数式接口)中只能有一个抽象方法。
函数式接口:接口中只有一个抽象方法的接口,通过注解@Functioninterface 限定
lambda操作符将lambda表达式拆成两个部分:
(1)左侧是lambda表达式的参数列表(可以理解为抽象方法的参数)
(2)右侧是lambda表达式中需要执行的功能,即lambda体(可以理解为抽象方法的实现的功能)
3.1 格式一:无参数,无返回值
()->System.out.println("Hello Lambda!");
3.2 格式二:有一个参数,无返回值(如果只有一个参数可以省略(),如果只有一条语法可以省略{})
(x)->System.out.println(x);
3.3 格式三:如果只有一个参数,小括号可以省略不写
x -> System.out.println(x);
3.4 格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
Comparator<Integer> com = (x, y) -> {
System.out.println("函数式接口");
return Integer.compare(x, y);
};
3.5 格式五:如果 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
3.6 格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即“类型推断”
(Integer x, Integer y) -> Integer.compare(x, y);
4 内置的四个核心函数式接口
4.1 消费型接口Consumer<T>
抽象方法:void accept(T t);
//消费型接口
@Test
public void test1() {
// Consumer<Double> con = (x)-> System.out.print(x);
// consumer(100, con);
consumer(100,(x)-> System.out.print(x));
}
public void consumer(double money,Consumer<Double> con){
con.accept(money);
}
4.2 供给型接口Supplier<T>
抽象方法:T get();
//供给型接口
@Test
public void test2() {
List<Integer> randomNum = getRandomNum(10,()->new Random().nextInt(100));
System.out.println(randomNum);
}
/**
* @Description 生成指定个数的随机数
* @param count
* @param su
* @return int
*/
public List<Integer> getRandomNum(int count,Supplier<Integer> su) {
List<Integer> list = new ArrayList<>();
for(int i = 0;i<count;i++) {
list.add(su.get());
}
return list;
}
4.3 函数式接口Function<T,R>
抽象方法:R apply(T t);
//函数式型接口
@Test
public void test3() {
List<String> list = toUpperList(new ArrayList<String>(Arrays.asList("abc","df","aa","cc")),
(x)-> x.toUpperCase()
);
System.out.println(list);
}
/**
* @Description 将集合里面的字符串转大写
* @param list
* @param fun
* @return List<String>
*/
public List<String> toUpperList(List<String> list,Function<String,String> fun){
for(int i = 0;i < list.size();i++) {
String apply = fun.apply(list.get(i));
list.set(i, apply);
}
return list;
}
4.4 断言型接口Predicate<T>
抽象方法:boolean test(T t);
//断言型接口
@Test
public void test4() {
List<String> list = Remove(new ArrayList<String>(Arrays.asList("abc","df","aa","cc")),
(s) -> s.length() < 2);
System.out.println(list);
}
/**
* @Description 删除集合中长度小于2的元素
* @param list
* @param pre
* @return List<String>
*/
public List<String> Remove(List<String> list,Predicate<String> pre) {
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String str = it.next();
if(pre.test(str)) {
list.remove(str);
}
}
return list;
}
5 方法引用
5.1 方法引用
将Lambda体中的功能,已有方法方法提供了实现,可以使用方法引用(方法引用可以理解为Lambda的另一种表现形式)
方法引用分类:
(1)对象的引用::实例方法名
//对象的引用::实例方法名
@Test
public void test1() {
Date date = new Date();
//传统lambda表达式
Supplier<Long> su = ()-> date.getTime();
System.out.println(su.get());
//对象引用
Supplier<Long> su1 = date::getTime;
System.out.println(su1.get());
}
(2)类名::静态方法名
//类名::静态方法名
@Test
public void test2() {
Comparator<Integer> com = (x,y)-> Integer.compare(x, y);
System.out.println(com.compare(4, 5));
Comparator<Integer> com1 = Integer::compare;
System.out.println(com1.compare(4, 5));
}
(3)类名::实例方法名
//类名::实例方法名
@Test
public void test3() {
Function<Person,String> fun = (x)-> x.show();
System.out.println(fun.apply(new Person()));
Function<Person,String> fun1 = Person::show;
System.out.println(fun.apply(new Person()));
}
class Person{
public String show() {
return "aaa";
}
}
注意:方法引用所引用的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致。若Lambda的参数列表的第一个参数,是实例方法的调用,第二个参数(或无参)是实例方法的参数是,格式:ClassName::MethodName
5.2 构造器引用
构造器中的参数列表需要与函数式中参数列表保持一致
类名::new
//类名 :: new
@Test
public void test4() {
Supplier<Person> sup = () -> new Person();
System.out.println(sup.get());
Supplier<Person> sup2 = Person::new;
System.out.println(sup2.get());
}
6 Stream
6.1 什么是Stream
Stream流是数据管道,用于操作数据源(集合,数组)所产生的元素序列。
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,需要对元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。Stream是单向的数据只能遍历一次。迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。
注意:①stream自己不会存储元素;②stream不会改变对象,他会返回一个新的stream;③stream的操作是延迟的。
6.2 Stream的操作
操作步骤:①创建Stream;②中间操作;③终止操作
(1)创建流
@Test
public void test1() {
//1.Collection获取流,获取到的是顺序流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
//2.通过Arrays中的stream获取一个数组流
String[] arr = new String[5];
Stream<String> stream2 = Arrays.stream(arr);
//3.通过Stream类中的静态方法of
Stream<Integer> of = Stream.of(11,22,33,44,55);
//4.创建无限流
Stream<Integer> limit = Stream.iterate(0, (x)-> x + 1).limit(20);
limit.forEach(System.out::println);
//5.用Stream的generate生成
Stream<Double> limit2 = Stream.generate(Math::random).limit(20);
limit2.forEach(System.out::println);
}
(2)中间操作:
常用中间操作方法如下:
方法 | 描述 |
Stream<T> filter(Predicate<? super T> predicate) | 接收Lambda,从流中排除某些元素 |
Stream<T> limit(long maxSize) | 截断流,使元素不超过给定数量 |
Stream<T> skip(long n) | 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个流,与limit(n)互补 |
Stream<T> distinct() | 帅选通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
<R> Stream<R> map(Function<? super T,? extends R> mapper) | 接收一个lambda,将元素转成成其他形式或提取信息,接收一个函数作为参数,该函数会被 应用到个元素上,并将其映射成一个新元素 |
(3)终止操作
常用终止操作如下
方法 | 描述 |
boolean allMatch(Predicate<? super T> predicate) | 检查是否匹配所有元素 |
boolean anyMatch(Predicate<? super T> predicate) | 检查是否至少匹配一个元素 |
boolean noneMatch(Predicate<? super T> predicate) | 检查是否没有匹配的元素 |
Optional<T> findFirst() | 返回第一个元素 |
Optional<T> findAny() | 返回当前流中的任意元素 |
long count() | 返回流中元素的总个数 |
Optional<T> max(Comparator<? super T> comparator) | 返回流中最大值 |
Optional<T> min(Comparator<? super T> comparator) | 返回流中最小值 |
案例:
@Test
public void test2() {
List<String> list = Arrays.asList("a","d","e","b","c");
//自然排序
list.stream()
.sorted()
.forEach(System.out::println);
//定制排序
List<Person> p = new ArrayList<>();
p.add(new Person("aa",18,100.0));
p.add(new Person("cc",16,150.0));
p.add(new Person("bb",20,180.0));
p.stream()
.sorted((e1,e2)->{
if(e1.getAge()==e2.getAge()) {
return e1.getName().compareTo(e2.getName());
}else {
return e1.getAge()-e2.getAge();
}
}).forEach(System.out::println);
}
class Person {
private String name;
private int age;
private double score;
public Person() {
}
public Person(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
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 getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Person1 [name=" + name + ", age=" + age + ", score=" + score + "]";
}
}