1.lambda & stream
什么是 Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。并行(parallel)程序
parallelStream 是流并行处理程序的代替方法。就是 stream的并发方法。limit
limit 方法用于获取指定数量的流。
public class User implements Comparable<User>{private long id;private String name;private int age;public long getId() {return id;}public void setId(long id) {this.id = id;}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;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(User user) {int num = this.age - user.getAge();if(num > 0){return 1;}else if(num == 0){return 0;}return -1;}}
1.1 遍历
@Testpublic void jdk8_1(){List<String> list = new ArrayList<String>();list.add("2");list.add("1");list.add("3");Collections.sort(list); //排序//直接循环输出for (String str :list){System.out.print(" " + str);}System.out.println();//采用lambda 表达式遍历list.forEach(item -> {System.out.print(" " + item);});//stream流遍历System.out.println();list.stream().forEach(item -> {System.out.print(" " + item);});}
1.2 排序
使用普通排序 要是实现Comparable
或者使用匿名内部类
@Testpublic void jdk8_2(){List<User> list = initList();/*** 1.使用的这个方法 User对象必须实现Comparable 接口*/Collections.sort(list);/*** 2.这种方式不需要实现Comparable 接口*/Collections.sort(list, new Comparator<User>() {@Overridepublic int compare(User o1, User o2) {return o1.getAge() - o2.getAge();}});/*** 3.使用lambda表达式 或者stream*/list.stream().sorted(Comparator.comparing(User::getAge));Collections.sort(list, Comparator.comparing(User::getAge));list.forEach(item -> {System.out.println(item.toString());});}
1.3 list转map
@Testpublic void jdk8_3(){List<User> list = initList();//1.传统遍历 不做介绍//2.lambda id:ObjectMap<Long, User> userMap = list.stream().collect(Collectors.toMap(user -> user.getId(), user -> user));userMap.entrySet().forEach(entry ->{System.out.println(entry.getKey() + " : " + entry.getValue().toString());});userMap.values().forEach(value ->{System.out.println(value.toString());});userMap.keySet().forEach(key ->{System.out.println(userMap.get(key).toString());});/*** 只保留list的某些字段*/List<Long> ids = list.stream().map(User::getId).collect(Collectors.toList());System.out.println(ids);/*** list转map 并分组*/Map<Integer, List<User>> groupMap = list.stream().collect(Collectors.groupingBy(user -> user.getAge()));System.out.println(groupMap.values());}
1.4 求和过滤
@Testpublic void jdk8_4(){List<User> list = initList();int sumAge = list.stream().collect(Collectors.summingInt(User::getAge));System.out.println(sumAge);//过滤List<User> list2 = list.stream().filter(user -> {return user.getAge() == 11;}).collect(Collectors.toList());System.out.println(list2);}
1.5 统计
@Testpublic void jdk8_5(){List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);IntSummaryStatistics stats = numbers.stream().mapToInt(item -> item).summaryStatistics();System.out.println("列表中最大的数 : " + stats.getMax());System.out.println("列表中最小的数 : " + stats.getMin());System.out.println("所有数之和 : " + stats.getSum());System.out.println("平均数 : " + stats.getAverage());}
2.optional
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
| 1 | static 返回空的 Optional 实例。 |
|---|---|
| 2 | boolean equals(Object obj) 判断其他对象是否等于 Optional。 |
| 3 | Optional 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。 |
| 4 | T get() 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException |
| 5 | void ifPresent(Consumer<? super T> consumer) 如果值存在则使用该值调用 consumer , 否则不做任何事情。 |
| 6 | boolean isPresent() 如果值存在则方法会返回true,否则返回 false。 |
| 7 | static 返回一个指定非null值的Optional。 |
| 8 | static 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。 |
| 9 | T orElse(T other) 如果存在该值,返回值, 否则返回 other。 |
@Testpublic void jdk8_4(){User user1 = null;User user2 = new User(1L, "张三", 12);Optional<User> userOptional1 = Optional.ofNullable(user1);Optional<User> userOptional2 = Optional.of(user2);System.out.println(userOptional2.isPresent());User user = userOptional2.get();}
3.方法引用
方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号 :: 。
/*** @author meikb* @desc ** @date 2020-05-11 14:18*/public class Fun_ref {@Testpublic void fun_1(){List<Car> cars = new ArrayList<>();Car car1 = new Car();Car car2 = new Car();Car car3 = new Car();cars.add(car1);cars.add(car2);cars.add(car3);cars.add(Car.create(Car::new));cars.forEach(Car::run);}}class Car {public void run(){System.out.println("run****************");}public static Car create(final Supplier<Car> supplier) {return supplier.get();}}
public class Fun_ref {@Testpublic void fun_1(){List<Car> cars = new ArrayList<>();Car car = new Car();Window win = new Window();car.run(Window::process);}}class Car {public Car(){System.out.println("hi");}public Object run(final Supplier<Object> supplier){try {System.out.println("lock");return supplier.get();}finally {System.out.println("unlock");}}}class Window{public static Object process(){System.out.println("process");return new Object();}}
输出:
hi
lock
process
unlock
就是说 car.run(Window::process); 这个段代码在执行get的的时候才会 执行Window 的 process 方法
4.函数式接口
| 1 | BiConsumer |
|---|---|
| 2 | BiFunction |
| 3 | BinaryOperator |
| 4 | BiPredicate |
| 5 | BooleanSupplier代表了boolean值结果的提供方 |
| 6 | Consumer |
| 7 | DoubleBinaryOperator代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。 |
| 8 | DoubleConsumer代表一个接受double值参数的操作,并且不返回结果。 |
| 9 | DoubleFunction |
| 10 | DoublePredicate代表一个拥有double值参数的boolean值方法 |
| 11 | DoubleSupplier代表一个double值结构的提供方 |
| 12 | DoubleToIntFunction接受一个double类型输入,返回一个int类型结果。 |
| 13 | DoubleToLongFunction接受一个double类型输入,返回一个long类型结果 |
| 14 | DoubleUnaryOperator接受一个参数同为类型double,返回值类型也为double 。 |
| 15 | Function |
| 16 | IntBinaryOperator接受两个参数同为类型int,返回值类型也为int 。 |
| 17 | IntConsumer接受一个int类型的输入参数,无返回值 。 |
| 18 | IntFunction |
| 19 | IntPredicate:接受一个int输入参数,返回一个布尔值的结果。 |
| 20 | IntSupplier无参数,返回一个int类型结果。 |
| 21 | IntToDoubleFunction接受一个int类型输入,返回一个double类型结果 。 |
| 22 | IntToLongFunction接受一个int类型输入,返回一个long类型结果。 |
| 23 | IntUnaryOperator接受一个参数同为类型int,返回值类型也为int 。 |
| 24 | LongBinaryOperator接受两个参数同为类型long,返回值类型也为long。 |
| 25 | LongConsumer接受一个long类型的输入参数,无返回值。 |
| 26 | LongFunction |
| 27 | LongPredicateR接受一个long输入参数,返回一个布尔值类型结果。 |
| 28 | LongSupplier无参数,返回一个结果long类型的值。 |
| 29 | LongToDoubleFunction接受一个long类型输入,返回一个double类型结果。 |
| 30 | LongToIntFunction接受一个long类型输入,返回一个int类型结果。 |
| 31 | LongUnaryOperator接受一个参数同为类型long,返回值类型也为long。 |
| 32 | ObjDoubleConsumer |
| 33 | ObjIntConsumer |
| 34 | ObjLongConsumer |
| 35 | Predicate |
| 36 | Supplier |
| 37 | ToDoubleBiFunction |
| 38 | ToDoubleFunction |
| 39 | ToIntBiFunction |
| 40 | ToIntFunction |
| 41 | ToLongBiFunction |
| 42 | ToLongFunction |
| 43 | UnaryOperator |
public class JDK8_Fun {@Testpublic void fun_1(){List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);// Predicate<Integer> predicate = n -> true// n 是一个参数传递到 Predicate 接口的 test 方法// n 如果存在则 test 方法返回 trueSystem.out.println("输出所有数据:");// 传递参数 neval(list, n->true);// Predicate<Integer> predicate1 = n -> n%2 == 0// n 是一个参数传递到 Predicate 接口的 test 方法// 如果 n%2 为 0 test 方法返回 trueSystem.out.println("输出所有偶数:");eval(list, n-> n%2 == 0 );// Predicate<Integer> predicate2 = n -> n > 3// n 是一个参数传递到 Predicate 接口的 test 方法// 如果 n 大于 3 test 方法返回 trueSystem.out.println("输出大于 3 的所有数字:");eval(list, n-> n > 3 );}public void eval(List<Integer> list, Predicate<Integer> predicate) {for(Integer n: list) {if(predicate.test(n)) {System.out.print(n + " ");}}System.out.println();}}
5.默认方法
接口内部定义方法 使用default
public interface Vehicle {default void print(){System.out.println("我是一辆车!");}}
6.新日期
Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
- 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
- 设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
- 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
- Local(本地) − 简化了日期时间的处理,没有时区的问题。
Zoned(时区) − 通过制定的时区处理日期时间。
```java @Test public void testLocalDateTime(){// 获取当前的日期时间 LocalDateTime currentTime = LocalDateTime.now(); System.out.println(“当前时间: “ + currentTime); //当前时间: 2020-05-11T14:53:10.011
LocalDate date1 = currentTime.toLocalDate(); System.out.println(“date1: “ + date1); //date1: 2020-05-11
Month month = currentTime.getMonth(); int day = currentTime.getDayOfMonth(); int seconds = currentTime.getSecond();
//月: MAY, 日: 11, 秒: 10 System.out.println(“月: “ + month +”, 日: “ + day +”, 秒: “ + seconds);
LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);System.out.println("date2: " + date2); //2012-05-10T14:53:10.011// 12 december 2014LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);System.out.println("date3: " + date3); //2014-12-12// 22 小时 15 分钟LocalTime date4 = LocalTime.of(22, 15);System.out.println("date4: " + date4); //22:15// 解析字符串LocalTime date5 = LocalTime.parse("20:15:30");System.out.println("date5: " + date5); //20:15:30
}
输出:```java当前时间: 2020-05-11T14:53:10.011date1: 2020-05-11月: MAY, 日: 11, 秒: 10date2: 2012-05-10T14:53:10.011date3: 2014-12-12date4:date5:
7.fork/join
fork/join作为一个并发框架在jdk7的时候就加入到了我们的java并发包java.util.concurrent中,并且在java 8 的lambda并行流中充当着底层框架的角色。
fork/join大体的执行过程,先把一个大任务分解(fork)成许多个独立的小任务,然后起多线程并行去处理这些小任务。处理完得到结果后再进行合并(join)就得到我们的最终结果。
