Java8新特性
简介:
- 速度更快
- 代码更少(新的语法lambda表达式)
- 强大的stream api
- 最大化减少空指针异常 optional
1.Lamada表达式
1.1说明:
lambda是一个匿名函数,也可以理解为接口的实例,一段可以像数据一样传递的代码,使Java语言更灵活,更紧凑
1.2 语法:
使用了新的语法操作符 ‘->’ ,该操作符的左侧是lambda表达式的参数部分,右侧是lambda体
1.2.1 几种基本语法
* 格式一:无参,无返回值
* () -> System.out.println("hello lambda");
*
* 格式二:一个参数,无返回值(如果只有一个参数,则小括号可以不写:x -> System.out.println(x);)
* (x) -> System.out.println(x);
*
* 格式三:两个或以上的参数,有返回值,具lambda体中有多行语句,则需要放在{}中
* Comparator<Integer> comparator = (x,y) -> {
* System.out.println("格式三 hello");
* return x-y;
* };
* 格式四:如果lambda体中只有一条语句,则return和大括号{}可以不写
* Comparator<Integer> comparator1 = (x,y) -> Integer.compare(x,y);
*
* 格式五:lambda表达式的参数类型可以省略不写,JVM编译器可以根据上下文判断参数类型;
* lambda表达式需要函数式接口的支持
* 函数式接口:只有一个抽象方法的接口,可以使用@FunctionalInterface来检查是否是函数式接口,有该注解 修饰的接口,只能有一个抽象方法
*
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("hello world");
}
};
r.run();
System.out.println("===========");
Runnable r1 = () -> System.out.println("hello lambda");
r1.run();
System.out.println("格式二=====================");
Consumer<String> con = (x) -> System.out.println(x);
con.accept("hello 格式二");
System.out.println("格式三=====================");
Comparator<Integer> comparator = (x,y) -> {
System.out.println("格式三 hello");
return Integer.compare(x,y);
};
Comparator<Integer> comparator1 = (x,y) -> Integer.compare(x,y);
System.out.println("func=====================");
Integer num = operation(10, (x) -> x*x );
System.out.println(num);
}
2.函数式接口
2.1定义:
只包含一个抽象方法的接口,是函数式接口,可以通过lambda表达式来创建该接口的对象,可以使用@FunctionalInterface注解来检查一个接口是否是函数式接口
2.2举例
//Collections.sort()通过这个方法,对emps进行排序,先按年龄排序,年龄相同按姓名比
public static void test(){
Collections.sort(emps,(e1,e2) -> {
if(e1.getAge() == e2.getAge()){
return e1.getName().compareTo(e2.getName());
} else {
return Integer.compare(e1.getAge(),e2.getAge());
}
});
for(Employee emp: emps){
System.out.println(emp.toString());
}
}
2.3 四大内置核心函数式接口
- Consumer :消费性接口 void accept(T t) ;
- Supplier :供给型接口 T get();
- Function<T,R>:函数型接口 R apply(T t) ;
- Predicate :断言型接口 boolean test(T t);
public class TestLambda4 {
public static void main(String[] args) {
Comsume con = new Comsume();
double money = 1000.0;
con.test(money,(x)-> System.out.println("消费:"+x));
Supply supply = new Supply();
Integer num = 10;
List<Integer> list = supply.getList(num, () -> (int) (Math.random() * 10));
System.out.println(list.toString());
Predict predict = new Predict();
boolean test = predict.test(120, (x) -> 100 < x && x < 200);
System.out.println(test);
}
}
//消费型接口
class Comsume{
public void test(double money, Consumer<Double> consumer){
consumer.accept(money);
}
}
//供给型接口
class Supply{
//产生指定个数的整数,并放入到集合中
public List<Integer> getList(Integer num, Supplier<Integer> supplier){
List<Integer> list = new ArrayList<>();
for (int i=0;i<num;i++){
Integer integer = supplier.get();
list.add(integer);
}
return list;
}
}
//断言型接口
class Predict{
//判断所给数据,是否在某个范围内
public boolean test(Integer num, Predicate<Integer> predicate){
return predicate.test(num);
}
}
3.方法引用与构造器
-
说明:若lambda体中内容有方法已经实现了,可以使用方法引用,可以理解为lambda的另一种表现形式
-
注意:lambda体中调用方法的参数列表与返回值类型,要与函数式接口抽象方法的参数和返回值一致;
若lambda体中,参数列表的第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用 类名::实例方法名
-
方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来
三种情况:
- 对象 :: 实例方法
- 类 :: 静态方法
- 类 :: 实例方法
-
构造器引用
格式:ClassName::new
注意:调用哪个构造器,取决于函数式接口的抽象方法的参数,根据参数列表取决于调用哪个构造方法;
class Test1{
//对象 :: 实例方法
public void test01(String str){
//Consumer<String> consumer = (x) -> System.out.println(x);
Consumer<String> consumer1 = System.out::println;
consumer1.accept(str);
}
//对象 :: 实例方法
public Integer getAge(){
Employee emp = new Employee();
Supplier<Integer> supplier = emp::getAge;
return supplier.get();
}
//类 :: 静态例方法
public void test03(){
// Comparator<Integer> comparator = (x,y)-> Integer.compare(x,y);
Comparator<Integer> comparator = Integer::compare;
int compare = comparator.compare(10, 20);
System.out.println(compare);
}
//类 :: 实例方法
//若lambda体中,参数列表的第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用 类名::实例方法名
public void test04(){
BiPredicate<String,String> bp = (x,y)->x.equals(y);
BiPredicate<String,String> bp1 = String::equals;
}
//构造器引用
public void test05(){
Supplier<Employee> sp = ()-> new Employee();
Supplier<Employee> sp1 = Employee::new;
sp1.get();
}
public void test06(){
Function<Integer,String[]> f = (x)->new String[x];
Function<Integer,String[]> f1 = String[]::new;
String[] apply = f1.apply(10);
System.out.println(apply.length);
}
}
4.Stream API
-
说明:流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列,集合讲的是数据,流讲的是计算!
-
注意:
- Stream 不会存储元素
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
-
Stream操作的三个步骤
-
创建流:一个数据源(如:集合、数组),获取一个流
/*创建流 Java8 中的 Collection 接口被扩展,提供了两个获取流的方法: default Stream<E> stream() : 返回一个顺序流 default Stream<E> parallelStream() : 返回一个并行流 Java8 中的 Arrays 的静态方法 stream() 可以获取数组流 static <T> Stream<T> stream(T[] array): 返回一个流 有值创建流:可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数 public static<T> Stream<T> of(T... values) : 返回一个流 有函数创建无限流:可以使用静态方法 Stream.iterate()和Stream.generate(), 创建无限流 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) public static<T> Stream<T> generate(Supplier<T> s) */ public void test01(){ //通过Collection系列集合提供的stream()或者parallelStream()来获取流 List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); //通过Arrays中的静态方法stream()获取数组流 String[] strs = new String[5]; Stream<String> stream1 = Arrays.stream(strs); //通过Stream的静态方法of()获取 Stream<String> aa = Stream.of("aa", "bb", "cc"); //创建无线流 //迭代 Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2); iterate.limit(20).forEach(System.out::println); //生成 Stream<Double> generate = Stream.generate(() -> Math.random()); generate.forEach(System.out::println); }
-
中间操作:一个中间操作链,对数据源的数据进行处理
中间操作可以连接起来形成一个流水线,中间操作不会执行任何处理,在终止操作时,执行全部处理
//中间操作 /* 筛选与切片 filter -- 接收lambda,从流中排除某些元素 limit -- 截断流,使其元素不超过给定的数量(如果过滤到) skip(n) -- 跳过元素,返回一个排除掉前n个元素的流,如果元素个数不足n个,则返回一个空流,与limit(n) 对应 distinct -- 筛选,通过流所生成元素的hashcode()和equals()去除重复元素 映射: map :接收lambda,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素, 排序: sorted():自然排序 sorted(Comparator com):订制排序 */ public void test02(){ //中间操作可以连接起来形成一个流水线,中间操作不会执行任何处理,在终止操作时,执行全部处理 Stream<Employee> stream = emps.stream().filter(employee -> { System.out.println("Stream 中间操作"); return employee.getAge() > 22; }); stream.forEach(System.out::println); } public void test03(){ emps.stream() .filter(employee -> { System.out.println("短路"); return employee.getAge()>18; }) .limit(2) .forEach(System.out::println); } public void test04(){ emps.stream() .filter((e) -> e.getAge()>22) .skip(2) .forEach(System.out::println); } //map映射 public void test05(){ List<String> list = Arrays.asList("aaa","bbb","ccc","ddd"); list.stream().map(str -> str.toUpperCase()).forEach(System.out::println); emps.stream().map(employee -> employee.getName()).forEach(System.out::println); } //排序 public void test06(){ List<String> list = Arrays.asList("eee","bbb","aaa","ccc","ddd"); list.stream().sorted().forEach(System.out::println); emps.stream().sorted( (emp1,emp2)-> { if(emp1.getAge() == emp2.getAge()){ return emp1.getName().compareTo(emp2.getName()); } return Integer.compare(emp1.getAge(),emp2.getAge()); }).forEach(System.out::println); }
-
终止操作:一个终止操作,执行中间操作链,并产生结果
/* * 查找与匹配 * allMatch:检查是否匹配所有元素 * anyMatch:检查是否至少匹配一个元素 * noneMatch:检查是否没有匹配所有元素 * findFirst:返回第一个元素 * findAny:返回任意元素 * count:返回元素总个数 * max:返回流中最大值 * min:返回流中最小值 * * 规约: * reduce(T identity, BinaryOprator) / reduce(BinaryOprator) :可以将流中的元素反复结合 * 起来,得到一个值 * map-reduce模式:map和reduce连用 * * 收集: * collect(Collector c):将流转为其他形式,接收一个Collector接口的实现,用于给stream中的元素做汇总的方法,Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例, */ class Test2{ List<Employee> emps = Arrays.asList( new Employee(100,"张三",20,5000.00, Employee.State.BUSY), new Employee(100,"李四",22,6000.00,Employee.State.FREE), new Employee(100,"王五",20,7000.00,Employee.State.BUSY), new Employee(100,"朱六",25,8000.00,Employee.State.VOCATION), new Employee(100,"样七",26,9000.00,Employee.State.BUSY) ); public void test(){ //allMatch匹配所有元素 boolean b = emps.stream().allMatch(e -> e.getState().equals(Employee.State.BUSY)); System.out.println(b); //anyMatch是否至少匹配一个元素 boolean b1 = emps.stream().anyMatch(e -> e.getState().equals(Employee.State.VOCATION)); System.out.println(b1); //noneMatch:检查是否没有匹配所有元素 boolean b2 = emps.stream().noneMatch(e -> e.getState().equals(Employee.State.VOCATION)); System.out.println(b2); //findFirst:返回第一个元素 Optional<Employee> first = emps.stream().sorted((e1, e2) -> -Double.compare(e1.getMoney(), e2.getMoney())).findFirst(); System.out.println(first.get()); Optional<Employee> first1 = emps.stream().sorted((e1, e2) -> -Double.compare(e1.getMoney(), e2.getMoney())).findAny(); System.out.println(first1.get()); //count:返回元素总个数 long count = emps.stream().count(); System.out.println(count); //max:返回流中最大值 Optional<Employee> max = emps.stream().max((e1, e2) -> Double.compare(e1.getMoney(), e2.getMoney())); System.out.println(max.get()); //max:返回流中最大值 Optional<Double> min = emps.stream().map(e -> e.getMoney()).min(Double::compare); System.out.println(min.get()); } public void test02(){ //reduce(T identity, BinaryOprator) / reduce(BinaryOprator) :可以将流中的元素反复结合起来,得到一个值 List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = list.stream().reduce(0, (x, y) -> x + y); System.out.println(sum); //map-reduce模式:map和reduce连用,Google用其进行网络搜索而得名 Optional<Double> reduce = emps.stream().map(e -> e.getMoney()).reduce((x, y) -> x + y); reduce = emps.stream().map(Employee::getMoney).reduce(Double::sum); System.out.println(reduce.get()); } //收集 public void test03(){ //将员工的名字收集到list集合中,可以 List<String> collect = emps.stream().map(e -> e.getName()).collect(Collectors.toList()); collect.forEach(System.out::println); //总数 Long sum = emps.stream().collect(Collectors.counting()); System.out.println(sum); //平均值 Double avg = emps.stream().collect(Collectors.averagingDouble(Employee::getMoney)); System.out.println(avg); //工资总和 Double sumS = emps.stream().collect(Collectors.summingDouble(Employee::getMoney)); System.out.println(sumS); //最大值 Optional<Employee> max = emps.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getMoney(), e2.getMoney()))); System.out.println(max.get()); //最小值 Optional<Double> min = emps.stream().map(e -> e.getMoney()).collect(Collectors.minBy(Double::compare)); System.out.println(min.get()); } //分组 public void test04(){ Map<Employee.State, List<Employee>> collect = emps.stream().collect(Collectors.groupingBy(Employee::getState)); System.out.println(collect); } }
收集:
-
- collect(Collector c):将流转为其他形式,接收一个Collector接口的实现,用于给stream中的元素做汇总的方法,Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,
方法 | 返回类型 | 作用 | 举例 |
---|---|---|---|
toList | List | 把流中元素收集到List | List emps= list.stream() .collect(Collectors.toList()); |
toSet | Set | 把流中元素收集到Set | List emps= list.stream() .collect(Collectors.toSet()); |
toCollection | Collection | 把流中元素收集到创建的集合 | Collectionemps=list.stream(). collect(Collectors.toCollection(ArrayList::new)); |
counting | Long | 计算流中元素的个数 | long count = list.stream().collect(Collectors.counting()); |
summingInt | Integer | 对流中元素的整数属性求和 | int total = list.stream().collect(Collectors.summingInt(Employee :: getAge)); |
averagingInt | Double | 计算流中元素Integer属性的平均值 | Double avg = emps.stream().collect(Collectors.averagingDouble(Employee::getMoney)); |
joining | String | 连接流中的每个字符串 | String str= list.stream().map(Employee::getName).collect(Collectors.joining()); |
maxBy,minBy | Optional | 选择最大最小值 | Optional max = list.stream().collect(Collectors.maxBy(comparingDouble(Employee::getMoney))) |