Java8新特性——lambda表达式和Stream类
一、lambda表达式
Lambda表达式本身就是一个接口的实现。
Lambda 表达式是 JDK8 的一个新特性,可以取代接口的匿名内部类,写出更优雅的 Java 代码(相当于语法糖)。
若想使用lambda简化接口的匿名内部类,需要该接口是一个功能接口
- 有且仅有一个必须要实现的抽象方法的接口称之为功能接口(Functional Interface)
- 功能接口有一个专门的注解标记它,写在接口声明的上面,就叫**@FunctionalInterface**
格式:(argument) -> {body}
书写规则 - 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
- 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, String b)
- 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
- Lambda 表达式的主体可包含零条或多条语句
如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致 - 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空。
Java内置的四种接口:
- Consumer< T >con 消费性 接口: void accept(T t); 接收一个参数,然后"消费"它
- Supplier< T >sup供给型接口 : T get(); 不接受参数,只有返回值,“雷锋”
- Function< T , R >fun 函数式接口 : R apply (T t); 接收一个参数,处理后返回另一个类型数据
- Predicate< T >: 断言形接口 : boolean test(T t); 接收一个参数,返回布尔值,作用是进行筛选
Consumer的使用
public class Test1 {
public static void main(String[] args) {
//lambda表达式本质上就是实现了接口中的抽象方法
makeMoney(1000,num -> System.out.println("这个懒货消费类"+num+"元"));
}
public static void makeMoney(double num, Consumer<Double>con){
con.accept(num);
}
}
Supplier的使用
public class Test1 {
public static void main(String[] args) {
final ArrayList<Integer> list = new Test1().getNumList(10, () -> new Random().nextInt(100));
list.forEach(System.out::println);
}
//写一个方法,把要实现的接口传入参数中,实际使用时用lambda表达式去实现该接口
public ArrayList<Integer> getNumList(int num, Supplier<Integer> sup){
final ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
final Integer e = sup.get();
list.add(e);
}
return list;
}
}
Function的使用
public class Test1 {
public static void main(String[] args) {
//只有一个参数,编译器很容易推断出是对其进行处理,故可以直接写方法名
System.out.println(new Test1().stringHander("haHHjfsjk", String::toUpperCase));
System.out.println(new Test1().stringHander(" hfa ", String::trim));
}
public String stringHander(String str,Function<String,String>fun){
return fun.apply(str);
}
}
Predicate的使用
public class Test1 {
public static void main(String[] args) {
final List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
final ArrayList<Integer> integers = new Test1().selectEven(list, x -> x % 2 == 0);
integers.forEach(System.out::println);
}
public ArrayList<Integer> selectEven(List<Integer> list, Predicate<Integer>pre){
final ArrayList<Integer> list1 = new ArrayList<>();
for (Integer integer : list) {
if(pre.test(integer)) list1.add(integer);
}
return list1;
}
}
二、Stream类
Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
- stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
- stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
- stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
常用创建的方式
1、通过 java.util.Collection.stream() 方法用集合创建流
List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
2、使用**java.util.Arrays.stream(T[] array)**方法用数组创建流
int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);
3、使用Stream的静态方法:of()、iterate()、generate()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println);
Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);
stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流。
Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。Stream的遍历、匹配非常简单。
3.1 遍历/匹配(foreach/find/match)
3.2 筛选(filter)返回true或false
3.3 聚合(max/min/count)
3.4 映射(map/flatMap) 一个流映射到另一个流
3.5 归约(reduce)
3.6 收集(collect) 将流中元素收集起来,返回一个集合或数组
3.6.2 统计(count/averaging)
3.6.3 分组(partitioningBy/groupingBy)
3.7 排序(sorted)
3.8 提取/组合