文章目录
lambda的语法
其实Lambda表达式的本质只是一个"语法糖",我们可以使用更少的代码来实现同样的功能,它允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
表达式形式
(parameters) -> expression
(parameters) -> {statments;}
() ->{} // 什么都不做
() ->{ "hello"; } 等价于 () ->{ return "hello"; }
Lambda表达式几个简单例子
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值 (看起来像是返回void)
(String s) -> System.out.print(s)
需要说明的是,不是每个接口都可以缩写成 Lambda 表达式。只有那些函数式接口(Functional Interface)才能缩写成 Lambda 表示式。
@FunctionalInterface
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
如定义了一个函数式接口如下:
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}
那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
常用的@FunctionalInterface在包java.util.function中 它包含了很多类,所如下
其接口描述有:
接口 | 描述 |
---|---|
BiConsumer<T,U> | 代表了一个接受两个输入参数的操作,并且不返回任何结果 |
BiFunction<T,U,R> | 代表了一个接受两个输入参数的方法,并且返回一个结果 |
BinaryOperator | 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 |
BiPredicate<T,U> | 代表了一个两个参数的boolean值方法 |
BooleanSupplier | 代表了boolean值结果的提供方 |
Consumer | 代表了接受一个输入参数并且无返回的操作 |
Function<T,R> | 接受一个输入参数,返回一个结果 |
Predicate | 接受一个输入参数,返回一个布尔值结果。 |
Supplier | 无参数,返回一个结果 |
我们通过使用Predicate来了解接口方法
已知有一个Apple类,它有weight属性和color属性,我们来通过重量来筛选出合格的苹果
public class LambdaUsage {
public static void main(String[] args) {
// list中有3个不同颜色重量的苹果
List<Apple> list = Arrays.asList(new Apple("green",120), new Apple("red",150),new Apple("yellow",80));
// filterByWeight方法中筛选 质量大于100的苹果 条件
List<Apple> result = filterByWeight(list, w -> w > 100);
// 或者用 stram filter 条件过滤, filter里填写Predict类型
List<Apple> result2 = list.stream().filter(p -> p.getWeight() > 100).collect(Collectors.toList());
System.out.println("result 1:"+result);
System.out.println("result 2:"+result2);
}
// 条件筛选出苹果
private static List<Apple> filterByWeight(List<Apple> source, LongPredicate predicate){
List<Apple> result = new ArrayList<>();
for (Apple a : source) {
if (predicate.test(a.getWeight()))
result.add(a);
}
return result;
}
}
输出结果
Supllier 生产者
它与Function不同,它不接受参数,直接为我们生产一个指定的结果,有点像生产者模式:
Supplier<String> supplier = String::new;
String s = supplier.get();
System.out.println(s);
Supplier 接口有一个专属符号“::”,它表示直接返回一个值;
Consumer消费者
我们需要提供入参,用来被消费,没有返回值,如下面这段示例代码:
Consumer<Apple> ap = a -> System.out.println("the color of "+a.getColor()+" apple have"+ a.getWeight()+" kg");
ap.accept(new Apple("red",100));
Optional,避免空指针问题
空指针异常是我们开发常见的问题,通常我们会写大量的非空条件判断来避免空指针问题。Java 8引入了一个新的Optional类,很好的用于避免空指针的出现,下图为Optinal类的所有方法:
- 使用Optional创建类
// 创建一个空对象果篮
Optional<Basket> basket1 = Optional.empty();//Optional.of(new Basket("bamboo-basket",10,null));
// 创建一个非null的果篮Optional
Optional<Basket> basket2 = Optional.of(new Basket("bamboo-basket",10, Arrays.asList(new Apple("red",120),new Apple("blue",100))));
// 判断果篮是否为空值果篮 System.out.println("basket:"+basket1.isPresent());
- get()获取对象
// 获取basket
Basket basket = basket2.get();
- 判断对象是否为空 isPresent
// 判断果篮是否为空,不为空就输出果篮
if (basket2.isPresent())
System.out.println(basket2.toString());
- 如果不为空,则执行ifPresent
// 如果这个篮子不为空,执行lambda表达式,反之空的就不会执行
basket1.ifPresent((bask)->{
System.out.println("这个篮子是:"+bask.toString());
});
- 如果对象为空,就指定一个对象 orElse()
// 如果对象为空,则将它指定为一个塑料篮子
List<Apple> list = Arrays.asList(new Apple("red",200), new Apple("blue",220), new Apple("yellow",210));
// 原来不存在,所以给它一个新的值
Basket basket11 = basket1.orElse(new Basket("plastic-basket", 10, list));
System.out.println("new basket11:"+basket11.toString());
// 原来存在,直接赋原来值
Basket basket21 = basket2.orElse(new Basket("plastic-basket", 10, list));
System.out.println("new basket21:"+basket21.toString());
- 如果为空,抛出异常 orElseThrow
// 如果果篮为空,抛出异常
basket1.orElseThrow(IllegalStateException::new);
- filter过滤
// 判断出有苹果的果篮
basket2.filter(basket -> {return basket.getList().size()>0;}).ifPresent((basket)->{
System.out.println("果篮里有水果");
- 综上代码
public class OptionalTest {
public static void main(String[] args) {
// 创建一个空对象果篮
Optional<Basket> basket1 = Optional.empty();//Optional.of(new Basket("bamboo-basket",10,null));
// 创建一个非null的果篮Optional
Optional<Basket> basket2 = Optional.of(new Basket("bamboo-basket",10, Arrays.asList(new Apple("red",120),new Apple("blue",100))));
// 判断果篮是否为空值果篮
System.out.println("basket:"+basket1.isPresent());
// 判断果篮是否为空,不为空就输出果篮
if (basket2.isPresent())
System.out.println(basket2.toString());
// 如果这个篮子不为空,执行lambda表达式,反之空的就不会执行
basket1.ifPresent((bask)->{
System.out.println("这个篮子是:"+bask.toString());
});
// 如果对象为空,则将它指定为一个塑料篮子
List<Apple> list = Arrays.asList(new Apple("red",200), new Apple("blue",220), new Apple("yellow",210));
// 原来不存在,所以给它一个新的值
Basket basket11 = basket1.orElse(new Basket("plastic-basket", 10, list));
System.out.println("new basket11:"+basket11.toString());
// 原来存在,直接赋原来值
Basket basket21 = basket2.orElse(new Basket("plastic-basket", 10, list));
System.out.println("new basket21:"+basket21.toString());
// 如果果篮为空,抛出异常
basket1.orElseThrow(IllegalStateException::new);
// 判断出有苹果的果篮
basket2.filter(basket -> {return basket.getList().size()>0;}).ifPresent((basket)->{
System.out.println("果篮里有水果");
});
}
}
水果篮子类
// 水果篮
class Basket{
private String name;
private int size;
private List<Apple> list;
public Basket(String name, int size, List<Apple> list) {
this.name = name;
this.size = size;
this.list = list;
}
// getter and setter...
@Override
public String toString() {
return "Basket{" +
"name='" + name + '\'' +
", size=" + size +
", list=" + list +
'}';
}
}
Stream流
当我们使用一个流的时候,通常包括三个基本步骤:
获取一个数据源(source)→ 数据转换→执行操作获取想要的结果;
每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换);
这就允许对其操作可以像链条一样排列,变成一个管道;
几种常用的stream
- map构造平方数
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
List<Integer> squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList());
System.out.println("平方数:"+squareNums);
这里的map是一种映射关系,每输入一个元素,都按照规则转换成另外一个元素
- filter 筛选出偶数
List<Integer> test = list.stream().filter(integer -> {
return integer % 2 == 0;
}).collect(Collectors.toList());
System.out.println(test);
list为1到30的整数,通过筛选留下来满足规则的元素
- sorted 倒排序
List<Integer> test2 = list.stream().sorted((a, b) -> {
return b-a;
}).collect(Collectors.toList());
System.out.println("test2:"+test2);
- Match匹配,这里分三种情况
1、allMatch:Stream 中全部元素符合传入的 predicate,返回 true
2、anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
3、noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
- 全部都是偶数
boolean b = list.stream().allMatch(integer -> {
return integer % 2 == 0;
});
System.out.println("全部是偶数:"+b);
- 存在偶数
b = list.stream().anyMatch(integer -> {
return integer % 2 == 0;
});
System.out.println("存在偶数:"+b);
- 全部都不是偶数
b = list.stream().noneMatch(integer -> {
return integer % 2 == 0;
});
System.out.println("都不是偶数:"+b);
- forEach 每个数字前加上小星星
System.out.println("每个数字前加上一个\"*\":");
list.stream().forEach(integer -> System.out.print("*"+integer+" "));
System.out.println("");
- reduce
这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。
从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的reduce。
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println("1:"+concat);
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println("2:"+minValue);
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
System.out.println("3:"+sumValue);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println("4:"+sumValue);
// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
System.out.println("5:"+concat);
- 完整代码
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
list.add(i);
}
/**
* 最常见的几种构造stream
*/
// map 平方数,这里的map是一种映射关系,每输入一个元素,都按照规则转换成另外一个元素
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
List<Integer> squareNums = nums.stream().map(n -> n * n).
collect(Collectors.toList());
System.out.println("平方数:"+squareNums);
// filter 筛选出偶数,通过筛选留下来满足规则都元素
List<Integer> test = list.stream().filter(integer -> {
return integer % 2 == 0;
}).collect(Collectors.toList());
System.out.println(test);
// 倒排序
List<Integer> test2 = list.stream().sorted((a, b) -> {
return b-a;
}).collect(Collectors.toList());
System.out.println("test2:"+test2);
// Match,这里有3种情况
/**
* allMatch:Stream 中全部元素符合传入的 predicate,返回 true
* anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
* noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
*/
// 全都是偶数
boolean b = list.stream().allMatch(integer -> {
return integer % 2 == 0;
});
System.out.println("全部是偶数:"+b);
// 存在偶数
b = list.stream().anyMatch(integer -> {
return integer % 2 == 0;
});
System.out.println("存在偶数:"+b);
// 都不是偶数
b = list.stream().noneMatch(integer -> {
return integer % 2 == 0;
});
System.out.println("都不是偶数:"+b);
// forEach ,每个数字前加上一个"*"
System.out.println("每个数字前加上一个\"*\":");
list.stream().forEach(integer -> System.out.print("*"+integer+" "));
System.out.println("");
/**
* 这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),
* 然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。
* 从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。
*/
// reduce
// 字符串连接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println("1:"+concat);
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println("2:"+minValue);
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
System.out.println("3:"+sumValue);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println("4:"+sumValue);
// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
System.out.println("5:"+concat);
}
其他
两个冒号代表什么
双冒号运算就是Java中的[方法引用],[方法引用]的格式是 类名::方法名。
一般是用作Lambda表达式
例如:
表达式
person -> person.getName();
可以替换成:
Person::getName
表达式
() -> new HashMap<>();
可以替换成:
HashMap::new
这种[方法引用]或者说[双冒号运算]对应的参数类型是Function<T,R> T表示传入类型,R表示返回类型。