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 方法用于获取指定数量的流。
  1. public class User implements Comparable<User>{
  2. private long id;
  3. private String name;
  4. private int age;
  5. public long getId() {
  6. return id;
  7. }
  8. public void setId(long id) {
  9. this.id = id;
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public int getAge() {
  18. return age;
  19. }
  20. public void setAge(int age) {
  21. this.age = age;
  22. }
  23. @Override
  24. public String toString() {
  25. return "User{" +
  26. "id=" + id +
  27. ", name='" + name + '\'' +
  28. ", age=" + age +
  29. '}';
  30. }
  31. @Override
  32. public int compareTo(User user) {
  33. int num = this.age - user.getAge();
  34. if(num > 0){
  35. return 1;
  36. }else if(num == 0){
  37. return 0;
  38. }
  39. return -1;
  40. }
  41. }

1.1 遍历

  1. @Test
  2. public void jdk8_1(){
  3. List<String> list = new ArrayList<String>();
  4. list.add("2");
  5. list.add("1");
  6. list.add("3");
  7. Collections.sort(list); //排序
  8. //直接循环输出
  9. for (String str :list){
  10. System.out.print(" " + str);
  11. }
  12. System.out.println();
  13. //采用lambda 表达式遍历
  14. list.forEach(item -> {
  15. System.out.print(" " + item);
  16. });
  17. //stream流遍历
  18. System.out.println();
  19. list.stream().forEach(item -> {
  20. System.out.print(" " + item);
  21. });
  22. }

1.2 排序

使用普通排序 要是实现Comparable 接口
或者使用匿名内部类

  1. @Test
  2. public void jdk8_2(){
  3. List<User> list = initList();
  4. /**
  5. * 1.使用的这个方法 User对象必须实现Comparable 接口
  6. */
  7. Collections.sort(list);
  8. /**
  9. * 2.这种方式不需要实现Comparable 接口
  10. */
  11. Collections.sort(list, new Comparator<User>() {
  12. @Override
  13. public int compare(User o1, User o2) {
  14. return o1.getAge() - o2.getAge();
  15. }
  16. });
  17. /**
  18. * 3.使用lambda表达式 或者stream
  19. */
  20. list.stream().sorted(Comparator.comparing(User::getAge));
  21. Collections.sort(list, Comparator.comparing(User::getAge));
  22. list.forEach(item -> {
  23. System.out.println(item.toString());
  24. });
  25. }


1.3 list转map

  1. @Test
  2. public void jdk8_3(){
  3. List<User> list = initList();
  4. //1.传统遍历 不做介绍
  5. //2.lambda id:Object
  6. Map<Long, User> userMap = list.stream().collect(Collectors.toMap(user -> user.getId(), user -> user));
  7. userMap.entrySet().forEach(entry ->{
  8. System.out.println(entry.getKey() + " : " + entry.getValue().toString());
  9. });
  10. userMap.values().forEach(value ->{
  11. System.out.println(value.toString());
  12. });
  13. userMap.keySet().forEach(key ->{
  14. System.out.println(userMap.get(key).toString());
  15. });
  16. /**
  17. * 只保留list的某些字段
  18. */
  19. List<Long> ids = list.stream().map(User::getId).collect(Collectors.toList());
  20. System.out.println(ids);
  21. /**
  22. * list转map 并分组
  23. */
  24. Map<Integer, List<User>> groupMap = list.stream().collect(Collectors.groupingBy(user -> user.getAge()));
  25. System.out.println(groupMap.values());
  26. }

1.4 求和过滤

  1. @Test
  2. public void jdk8_4(){
  3. List<User> list = initList();
  4. int sumAge = list.stream().collect(Collectors.summingInt(User::getAge));
  5. System.out.println(sumAge);
  6. //过滤
  7. List<User> list2 = list.stream().filter(user -> {
  8. return user.getAge() == 11;
  9. }).collect(Collectors.toList());
  10. System.out.println(list2);
  11. }

1.5 统计

  1. @Test
  2. public void jdk8_5(){
  3. List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
  4. IntSummaryStatistics stats = numbers.stream().mapToInt(item -> item).summaryStatistics();
  5. System.out.println("列表中最大的数 : " + stats.getMax());
  6. System.out.println("列表中最小的数 : " + stats.getMin());
  7. System.out.println("所有数之和 : " + stats.getSum());
  8. System.out.println("平均数 : " + stats.getAverage());
  9. }

2.optional

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。

1 static Optional empty()
返回空的 Optional 实例。
2 boolean equals(Object obj)
判断其他对象是否等于 Optional。
3 Optional filter(Predicate<? super predicate)
如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。
4 T get()
如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
5 void ifPresent(Consumer<? super T> consumer)
如果值存在则使用该值调用 consumer , 否则不做任何事情。
6 boolean isPresent()
如果值存在则方法会返回true,否则返回 false。
7 static Optional of(T value)
返回一个指定非null值的Optional。
8 static Optional ofNullable(T value)
如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
9 T orElse(T other)
如果存在该值,返回值, 否则返回 other。
  1. @Test
  2. public void jdk8_4(){
  3. User user1 = null;
  4. User user2 = new User(1L, "张三", 12);
  5. Optional<User> userOptional1 = Optional.ofNullable(user1);
  6. Optional<User> userOptional2 = Optional.of(user2);
  7. System.out.println(userOptional2.isPresent());
  8. User user = userOptional2.get();
  9. }

3.方法引用

方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号 :: 。

  1. /**
  2. * @author meikb
  3. * @desc *
  4. * @date 2020-05-11 14:18
  5. */
  6. public class Fun_ref {
  7. @Test
  8. public void fun_1(){
  9. List<Car> cars = new ArrayList<>();
  10. Car car1 = new Car();
  11. Car car2 = new Car();
  12. Car car3 = new Car();
  13. cars.add(car1);
  14. cars.add(car2);
  15. cars.add(car3);
  16. cars.add(Car.create(Car::new));
  17. cars.forEach(Car::run);
  18. }
  19. }
  20. class Car {
  21. public void run(){
  22. System.out.println("run****************");
  23. }
  24. public static Car create(final Supplier<Car> supplier) {
  25. return supplier.get();
  26. }
  27. }
  1. public class Fun_ref {
  2. @Test
  3. public void fun_1(){
  4. List<Car> cars = new ArrayList<>();
  5. Car car = new Car();
  6. Window win = new Window();
  7. car.run(Window::process);
  8. }
  9. }
  10. class Car {
  11. public Car(){
  12. System.out.println("hi");
  13. }
  14. public Object run(final Supplier<Object> supplier){
  15. try {
  16. System.out.println("lock");
  17. return supplier.get();
  18. }finally {
  19. System.out.println("unlock");
  20. }
  21. }
  22. }
  23. class Window{
  24. public static Object process(){
  25. System.out.println("process");
  26. return new Object();
  27. }
  28. }

输出:
hi
lock
process
unlock

就是说 car.run(Window::process); 这个段代码在执行get的的时候才会 执行Window 的 process 方法
image.png

4.函数式接口

1 BiConsumer代表了一个接受两个输入参数的操作,并且不返回任何结果
2 BiFunction代表了一个接受两个输入参数的方法,并且返回一个结果
3 BinaryOperator代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
4 BiPredicate代表了一个两个参数的boolean值方法
5 BooleanSupplier代表了boolean值结果的提供方
6 Consumer代表了接受一个输入参数并且无返回的操作
7 DoubleBinaryOperator代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。
8 DoubleConsumer代表一个接受double值参数的操作,并且不返回结果。
9 DoubleFunction代表接受一个double值参数的方法,并且返回结果
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接受一个int类型输入参数,返回一个结果 。
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接受一个long类型输入参数,返回一个结果。
27 LongPredicateR接受一个long输入参数,返回一个布尔值类型结果。
28 LongSupplier无参数,返回一个结果long类型的值。
29 LongToDoubleFunction接受一个long类型输入,返回一个double类型结果。
30 LongToIntFunction接受一个long类型输入,返回一个int类型结果。
31 LongUnaryOperator接受一个参数同为类型long,返回值类型也为long。
32 ObjDoubleConsumer接受一个object类型和一个double类型的输入参数,无返回值。
33 ObjIntConsumer接受一个object类型和一个int类型的输入参数,无返回值。
34 ObjLongConsumer接受一个object类型和一个long类型的输入参数,无返回值。
35 Predicate接受一个输入参数,返回一个布尔值结果。
36 Supplier无参数,返回一个结果。
37 ToDoubleBiFunction接受两个输入参数,返回一个double类型结果
38 ToDoubleFunction接受一个输入参数,返回一个double类型结果
39 ToIntBiFunction接受两个输入参数,返回一个int类型结果。
40 ToIntFunction接受一个输入参数,返回一个int类型结果。
41 ToLongBiFunction接受两个输入参数,返回一个long类型结果。
42 ToLongFunction接受一个输入参数,返回一个long类型结果。
43 UnaryOperator接受一个参数为类型T,返回值类型也为T。
  1. public class JDK8_Fun {
  2. @Test
  3. public void fun_1(){
  4. List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
  5. // Predicate<Integer> predicate = n -> true
  6. // n 是一个参数传递到 Predicate 接口的 test 方法
  7. // n 如果存在则 test 方法返回 true
  8. System.out.println("输出所有数据:");
  9. // 传递参数 n
  10. eval(list, n->true);
  11. // Predicate<Integer> predicate1 = n -> n%2 == 0
  12. // n 是一个参数传递到 Predicate 接口的 test 方法
  13. // 如果 n%2 为 0 test 方法返回 true
  14. System.out.println("输出所有偶数:");
  15. eval(list, n-> n%2 == 0 );
  16. // Predicate<Integer> predicate2 = n -> n > 3
  17. // n 是一个参数传递到 Predicate 接口的 test 方法
  18. // 如果 n 大于 3 test 方法返回 true
  19. System.out.println("输出大于 3 的所有数字:");
  20. eval(list, n-> n > 3 );
  21. }
  22. public void eval(List<Integer> list, Predicate<Integer> predicate) {
  23. for(Integer n: list) {
  24. if(predicate.test(n)) {
  25. System.out.print(n + " ");
  26. }
  27. }
  28. System.out.println();
  29. }
  30. }

5.默认方法

接口内部定义方法 使用default

  1. public interface Vehicle {
  2. default void print(){
  3. System.out.println("我是一辆车!");
  4. }
  5. }

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);

  1. LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
  2. System.out.println("date2: " + date2); //2012-05-10T14:53:10.011
  3. // 12 december 2014
  4. LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);
  5. System.out.println("date3: " + date3); //2014-12-12
  6. // 22 小时 15 分钟
  7. LocalTime date4 = LocalTime.of(22, 15);
  8. System.out.println("date4: " + date4); //22:15
  9. // 解析字符串
  10. LocalTime date5 = LocalTime.parse("20:15:30");
  11. System.out.println("date5: " + date5); //20:15:30

}

  1. 输出:
  2. ```java
  3. 当前时间: 2020-05-11T14:53:10.011
  4. date1: 2020-05-11
  5. 月: MAY, 日: 11, 秒: 10
  6. date2: 2012-05-10T14:53:10.011
  7. date3: 2014-12-12
  8. date4:
  9. date5:

7.fork/join

fork/join作为一个并发框架在jdk7的时候就加入到了我们的java并发包java.util.concurrent中,并且在java 8 的lambda并行流中充当着底层框架的角色。

fork/join大体的执行过程,先把一个大任务分解(fork)成许多个独立的小任务,然后起多线程并行去处理这些小任务。处理完得到结果后再进行合并(join)就得到我们的最终结果。