1.lambda表达式
1.1 实例引用
原始写法
Comparator<Integer> com=new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } };
lambda表达式写法
Comparator<Integer> com1=(x,y)->Integer.compare(x,y);
1.2 lambda表达式基础语法
Java8中引入了一个新的操作符“->”,箭头操作符或者lambda操作符
左侧:lambda表达式参数列表
右侧:lambda表达式中所需执行的功能,即lambda体
语法格式:
无参数,无返回值:
Runnable r=()-> System.out.println("hello lambda");
r.run();
一个参数,无返回值:(一个参数小括号可以不写)
Consumer<String> con=(x)-> System.out.println(x);
con.accept("hello");
有两个以上参数,有返回值:
Comparator<Integer> com1=(x,y)->{ return Integer.compare(x,y); };
如果lambda体中只有一条语句,return和大括号都可以省略不写
Comparator<Integer> com1=(x,y)->Integer.compare(x,y);
参数数据类型都可以省略不写,Java8会自动推断数据类型,lambda表达式需要函数式接口的支持
2.函数式接口
只有一个抽象方法的接口称为函数式接口,可以使用注解@FunctionalInterface修饰,可以检查接口是不是函数式接口
java8 内置的四大核心函数式接口
Consumer<T> :消费型接口
void accept(T t);
Supplier<T> :供给型接口
T get();
Function<T,R> :函数型接口
R apply(T t);
Predicate<T> :断言型接口
boolean test(T t);
3.方法引用与构造器
3.1 方法引用
若lambda体中的内容有方法已经实现了,我们可以使用方法引用(可以理解为方法引用是lambda表达式的另外一种表现形式)
语法格式
对象::实例方法名
类::静态方法名
类::实例方法名
第一种:
对象::实例方法名
Consumer<String> con=(x)-> System.out::println;
第二种:
类::静态方法名
Supplier<Integer> sup=emp::getAge;
第三种:
类::实例方法名
BiPredicate<String,String> bp=(x,y)->x.equals(y);
BiPredicate<String,String> bp2=String::equals;
3.2 构造器引用
Supplier<Student> sup = () -> new Student(); Supplier<Student> sup2 = Student::new;
注意:需要调用的构造器的参数列表需要与函数式接口中抽象方法的参数列表保持一致!
3.3 数组引用
Function<Integer,String[]> fun=(x)->new String[x]; Function<Integer,String[]> fun2=String[]::new;
4.stream流
4.1 概述
Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的操作。Stream 提供了一种高效且易于使用的处理数据的方式。
注意:
(1)stream自己不会存储元素
(2)stream不会改变源对象,相反,他们会返回一个持有结果的新stream
(3)stream操作是延迟执行的,这意味着他们会等到需要结果的时候才执行
4.2 stream的操作步骤
(1)创建stream
一个数据源(如:集合,数组),获取一个流
(2)中间操作
一个中间操作链,对数据源的数据进行处理
(3)终止操作
一个终止操作,执行中间操作链,并产生结果
4.3 创建stream
通过collection系列接口提供的stream()或parallelStream()
List<Integer> list=new ArrayList<>(); Stream<Integer> stream = list.stream();
通过Arrays中的静态方法stream()获取数组流
Student[]stu=new Student[10]; Stream<Student> stream = Arrays.stream(stu);
通过Stream类中的静态方法of()
Stream<String> stream = Stream.of("aa", "bb", "cc");
创建无限流
(1)迭代
Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
(2)生成
Stream.generate(()->Math.random()).limit(5).forEach(System.out::println);
4.4 中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
筛选与切片
filter——接收lambda,从流中排除某些元素(过滤出满足条件的元素)
stu.stream().filter(e->e.getSage()>30).forEach(System.out::println);
limit——截断流,使其元素不超过指定数量
stu.stream().filter(e->e.getSage()>30).limit(2).forEach(System.out::println);
skip(n)——跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
stu.stream().skip(3).filter(e->e.getSage()>30).limit(2).forEach(System.out::println);
distinct——筛选,通过流所生成的元素hashCode()和equals()去除重复元素
stu.stream().skip(3).distinct().filter(e->e.getSage()>30).limit(2).forEach(System.out::println);
注意:使用distinct要重写hashCode()和equals()方法
映射
map——接收lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
List<String> list=Arrays.asList("aaa","bbb","ccc","ddd");
list.stream().map(str->str.toUpperCase()).forEach(System.out::println);
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连成一个流
List<String> list = Arrays.asList("abc","bbb","cbd","ddd"); list.stream().flatMap(Test1::getCharacterByString).forEach(System.out::println); public static Stream<Character> getCharacterByString(String str) { List<Character> characterList = new ArrayList<>(); for (Character character : str.toCharArray()) { characterList.add(character); } return characterList.stream(); }
排序
sorted()——自然排序(Comparable)
list.stream().sorted().forEach(System.out::println);
sorted(Comparator com)——定制排序(Comparator)
stu.stream().sorted((e1,e2)-> e1.getSage().compareTo(e2.getSage())).forEach(System.out::println);
4.5 终止操作
查找与匹配
allMatch——检查是否匹配所有元素
boolean flag = stu.stream().allMatch(e -> e.getSsex().equals("男")); System.out.println(flag);//false
anyMatch——检查是否至少匹配一个元素
boolean flag = stu.stream().anyMatch(e -> e.getSsex().equals("男")); System.out.println(flag);//true
noneMatch——检查是否没有匹配所有元素
boolean flag = stu.stream().noneMatch(e -> e.getSsex().equals("男")); System.out.println(flag);//false
findFirst——返回第一个元素
Optional<Student> optional = stu.stream().sorted((e1, e2) -> Double.compare(e1.getSage(), e2.getSage())).findFirst(); Student student = optional.get(); System.out.println(student);//Student(sname=tianqi, sage=18, ssex=女)
findAny——返回当前流中的任意一个元素
Optional<Student> optional1 = stu.parallelStream().filter(e -> e.getSage() > 30).findAny(); Student student1 = optional1.get(); System.out.println(student1);//Student(sname=zhaoliu, sage=34, ssex=男)
count——返回流中元素的总个数
long count = stu.stream().count(); System.out.println(count);//5
max——返回流中的最大值
Optional<Student> max = stu.stream().max((e1, e2) -> Double.compare(e1.getSage(), e2.getSage())); System.out.println(max.get());//Student(sname=wangwu, sage=43, ssex=女)
min——返回流中的最小值
Optional<Integer> min = stu.stream().map(Student::getSage).min(Integer::compare); System.out.println(min.get());//18
归约
reduce(T identity,BinaryOperator)——可以将流中的元素反复结合起来,得到一个值
List<Integer> list = Arrays.asList(1,2,3,4,5); Integer sum = list.stream().reduce(0, (x, y) -> x + y); System.out.println(sum);//15
map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名
Optional<Integer> reduce = stu.stream().map(Student::getSage).reduce(Integer::sum); System.out.println(reduce.get());//151
收集
collect——将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中的元素做汇总的方法
TreeSet<String> set = stu.stream().map(Student::getSname).collect(Collectors.toCollection(TreeSet::new));
System.out.println(set); //[zhangsan, wangwu, zhaoliu, lisi, tianqi]
获取年龄的平均值
Double collect = stu.stream().collect(Collectors.averagingDouble(Student::getSage)); System.out.println(collect); //30.2
分组:
Map<String, List<Student>> map = stu.stream().collect(Collectors.groupingBy(Student::getSsex)); System.out.println(map); //{女=[Student(sname=wangwu, sage=43, ssex=女), Student(sname=lisi, sage=33, ssex=女), Student(sname=tianqi, sage=18, ssex=女)], 男=[Student(sname=zhangsan, sage=23, ssex=男), Student(sname=zhaoliu, sage=34, ssex=男)]}
分区:满足条件的一个区,不满足条件的一个区
Map<Boolean, List<Student>> map1 = stu.stream().collect(Collectors.partitioningBy(s -> s.getSage() > 40));
System.out.println(map1);
运行结果:
{false=[Student(sname=zhangsan, sage=23, ssex=男), Student(sname=zhaoliu, sage=34, ssex=男), Student(sname=lisi, sage=33, ssex=女), Student(sname=tianqi, sage=18, ssex=女)], true=[Student(sname=wangwu, sage=43, ssex=女)]}
4.6 Optional类
Optional类是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在optional可以更好的表达这个概念,并且可以避免空指针异常。
常用方法:
Optional.of(T t):创建一个Optional实例,不能构建null
Optional<Student> op = Optional.of(new Student()); Student student2 = op.get(); System.out.println(student2);//Student(sname=null, sage=null, ssex=null)
Optional.empty():创建一个空的Optional实例
Optional<Student> empty = Optional.empty();
Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例
Optional<Student> student3 = Optional.ofNullable(new Student()); System.out.println(student3.get());//Student(sname=null, sage=null, ssex=null)
isPresent():判断是否包含值
orElse(T t):如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值
map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
5.接口中的默认方法与静态方法
5.1 接口中的默认方法
Java8中允许接口用default修饰,并且有方法体。
public interface MyFun { default String getName(){ return "HHH"; } }
接口默认方法的“类优先”原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时,
- 选择父类中的方法,如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
- 接口冲突,如果一个父类接口提供了一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突
5.2 接口中的静态方法
在Java8新特性中可以在接口写静态方法:
public static void show(){ System.out.println("接口中的静态方法"); }
调用静态方法是通过接口名.方法名
6.新时间日期API
6.1 LocalDate&LocalTime&LocalDateTime
LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期,时间,日期和时间。他们提供了简单的日期或时间,并不包括当前的时间信息,也不包括与时区相关的信息。
通过now()打印出当前时间
LocalDateTime ldt = LocalDateTime.now(); System.out.println(ldt);//2021-05-25T14:08:18.335105700
我们可以用of方法来指定具体的某一天
LocalDateTime time = LocalDateTime.of(2021, 8, 21, 10, 20, 54); System.out.println(time);//2021-08-21T10:20:54
提供plusDays()、minusMonths()等方法可以对日期时间进行增减操作
6.2 Instant
时间戳是以Unix元年,1970年1月1日00:00:00到某个时间之间的毫秒值
Instant默认时区UTC时区,与当前时间相差8个小时
Instant now = Instant.now(); System.out.println(now);//2021-05-25T06:16:55.742665700Z
使用偏移运算到当前时间
Instant now = Instant.now(); OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8)); System.out.println(offsetDateTime);//2021-05-25T14:20:17.610278400+08:00
6.3 Duration&Period
Duration是用来计算两个时间之间的间隔
Instant instant = Instant.now(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Instant instant1 = Instant.now(); Duration duration = Duration.between(instant, instant1); System.out.println(duration.toMillis());//1000
Period用来计算两个日期直接的间隔
LocalDate date = LocalDate.of(1997,9,22); LocalDate date1 = LocalDate.now(); Period period = Period.between(date, date1); System.out.println(period.getYears());//23
6.4 TemporalAdjuster
TemporalAdjuster:时间校正器,有时我们可能需要获取,例如:将日期调整到“下个周日”等操作
LocalDateTime now1 = LocalDateTime.now(); TemporalAdjuster next = now1.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); System.out.println(next);//2021-05-31T14:44:03.948067100
6.5 DateTimeFormatter
DateTimeFormatter:格式化时间/日期
DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_DATE; LocalDateTime ldt=LocalDateTime.now(); String format = ldt.format(isoDateTime); System.out.println(format);//2021-05-25
自定义时间日期格式:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); LocalDateTime localDateTime=LocalDateTime.now(); String format = localDateTime.format(dtf); System.out.println(format);//2021/05/25 14:54:31
6.6 ZoneDate&ZoneTime&ZoneDateTime
操作时区
Set<String> set = ZoneId.getAvailableZoneIds(); set.forEach(System.out::println);//查看所有时区 LocalDateTime ldt=LocalDateTime.now(ZoneId.of("Asia/Shanghai")); System.out.println(ldt);//2021-05-25T15:02:12.987891100
构建带时区的时间日期
LocalDateTime localDateTime1=LocalDateTime.now(); ZonedDateTime zdt = localDateTime1.atZone(ZoneId.of("Japan")); System.out.println(zdt);//2021-05-25T15:07:42.475631200+09:00[Japan]