JAVA8(Lambda表达式、方法引用、Stream API、新时间日期API)

目录

 

1.Lambda表达式

为什么使用lambda:

使用Lambda表达式注意事项

函数式接口

方法引用(了解)

Stream API

新时间日期API


1.Lambda表达式

Lambda表达式可以看成是匿名内部类,Lambda允许把函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递,使用Lambda表达式可以使代码变的更加的间接紧凑。

匿名内部类简化成Lambda表达式:

     基本语法:

    <函数式接口>  <变量名> = (参数1,参数2...) -> {
                        //方法体
     };

  • 有参数把参数写在箭头前的()内,如果只有一个参数还可以将箭头省略
  • 如果方法体只有一个语句,可以把方法体的{}省略,如果多条语句,应该放在{}内

案例1:

public class Demo1 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行了");
            }
        };
        Runnable runnable1 = ()-> System.out.println("线程1执行了");
    }
}

为什么使用lambda:

如:集合中获取年龄大于20的员工信息,获取工资大于20000的员工信息,获取每个需求的员工信息都需要创建新方法,每个方法返回不同需求的员工集合,造成代码重复性大。如果在添加类似需求,需要再添加方法,这时我们可以使用策略模式进行代码简化,更好的是代码可以重用。

1.使用策略模式优化代码

策略模式代码及解释如下:

package day0818.demo1;

import java.util.ArrayList;
import java.util.List;

public class Demo2 {
    public static void main(String[] args) {
        ArrayList employees=new ArrayList<>();
        employees.add(new Employee("少泊", 26, 32000));
        employees.add(new Employee("发海", 28, 30000));
        employees.add(new Employee("真真", 18, 28000));
        employees.add(new Employee("本伟", 27, 25000));
        employees.add(new Employee("坤坤", 21, 18000));
        //未使用策略模式每个需求都要调用不同的方法,如下
        List employees1 = filterByAge(employees);
        List employees2 = filterBySalary(employees);
        //使用策略模式相同需求只需调用同一种方法,如下:
        filter(employees, new MyInter() {
            @Override
            public boolean test(Employee employee) {
                //若此条件为employee.getSalary()>20000,筛选出工资大于20000的员工
                if(employee.getAge()>20){//筛选出符合条件的员工
                    return true;
                }
                return false;
            }
        });

    }
    //以下两个方法代码重复较多
    public static List filterByAge(ArrayList empList){
        List list = new ArrayList<>();
        for (Employee employee : empList) {
            if (employee.getAge()>=25){
                list.add(employee);
            }
        }
        return list;
    }
    public static List filterBySalary(ArrayList empList){
        List list = new ArrayList<>();
        for (Employee employee : empList) {
            if (employee.getSalary()>=20000){
                list.add(employee);
            }
        }
        return list;
    }
    //改为策略模式,需要写功能接口,并将此接口作为方法的参数
    public static List filter(ArrayList empList,MyInter myInter){
        List list = new ArrayList<>();
        for (Employee employee : empList) {
            if (myInter.test(employee)){//调用此接口的test方法,判断是否满足条件
                list.add(employee);
            }
        }
        return list;
    }
}

MyInter接口:

package day0818.demo1;

public interface MyInter {
    boolean test(T t);
}

2.使用Lambda表达式优化策略模式

JAVA8(Lambda表达式、方法引用、Stream API、新时间日期API)_第1张图片

将此处代码优化成Lambda表达式:

JAVA8(Lambda表达式、方法引用、Stream API、新时间日期API)_第2张图片

使用Lambda表达式注意事项

Lambda引入了新的操作符:->(箭头操作符),->将表达式分成两部分
左侧:(参数1,参数2…)表示参数列表;
右侧:{}内部是方法体 

1、形参列表的数据类型会自动推断; 
2、如果形参列表为空,只需保留(); 
3、如果形参只有1个,()可以省略,只需要参数的名称即可; 
4、如果执行语句只有1句,且无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有1句; 
5、lambda不会生成一个单独的内部类文件; 
6、lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系统会自动添加,此后在修改该局部变量,会报错。

 

函数式接口

如果一个接口只有一个抽象方法,则该接口称之为函数式接口,函数式接口可以使用Lambda表达式,lambda表达式会被匹配到这个抽象方法上。

Java四个核心函数式接口:

函数式接口 参数类型 返回类型 说明
Consumer 消费型接口 T void void accept(T t);对类型为T的对象应用操作
Supplier 供给型接口 T T get(); 返回类型为T的对象
Function 函数型接口 T R R apply(T t);对类型为T的对象应用操作,并返回类型为R类型的对象。
Predicate 断言型接口 T boolean boolean test(T t);确定类型为T的对象是否满足条件,并返回boolean类型。

案例:

public class Demo3 {
    public static void main(String[] args) {
        //Consumer接口,有参无返回值
        consumer(1000,money-> System.out.println("本次蹦迪消费:"+money));
        consumer(2000,money-> System.out.println("本次聚餐消费:"+money));
        //Supplier接口,有参数有返回值
        int[] ints = supplier(10, () -> new Random().nextInt(100));
        System.out.println(Arrays.toString(ints));
        //Function韩属型接口
        String s1 = function("hello", s -> s.substring(1, 4));
        System.out.println(s1);
        //Predicate 断言型接口
        List strList = new ArrayList<>();
        strList.add("hello");
        strList.add("goodbye");
        strList.add("verygood");
        strList.add("hi");
        List list = predicate(strList, s -> s.length() >= 3);
        System.out.println(list.toString());


    }
    public static void consumer(double money, Consumer consumer){
        consumer.accept(money);
    }

    public static int[] supplier(int len, Supplier supplier){
        int[] arr = new int[len];
        for (int i=0;i function){
        String str1 = function.apply(str);

        return str1;
    }
    public static List predicate(List stringList, Predicate predicate){
        List list = new ArrayList<>();
        for (String s : stringList) {
            if(predicate.test(s)){
                list.add(s);
            }
        }

        return list;
    }
}

方法引用(了解)

法引用是lambda表达式的一种简写形式。 如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。

有四种形式:

  • 对象::实例方法

  • 类::静态方法

  • 类::实例方法

  • 类::new

对象::实例方法

public class Demo1 {
    public static void main(String[] args) {
        //对象::实例方法
        //条件1 如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。
        //条件2 方法的参数个数类型和返回值类型和接口中的方法一致
        Consumer consumer = s-> System.out.println(s);
        Consumer consumer1 = System.out::println;
        consumer1.accept("hello");
        // 类::静态方法
        //条件1 如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。
        //条件2 方法的参数个数类型和返回值类型和接口中的方法一致
        Comparator comparator = (o1,o2)->Integer.compare(o1,o2);
        Comparator comparator1 = Integer::compareTo;
        comparator(10,10,comparator1);
        //类::实例方法(了解)
        //条件1 如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。
        //条件2 接口中两个参数,有一个作为方法的调用者,另一个作为参数,返回类型一致
        BiPredicate p = (x,y)->x.equals(y);
        BiPredicate p1 = String::equals;
        boolean b = biPredicate("hello", "world", p1);
        System.out.println(b);
        //条件1 如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。
        //条件2 接口中一个参数,参数作为方法的调用者,返回类型一致
        Function function = e->e.getName();
        Function function1 = Employee::getName;
        function(new Employee("张三",25,20000,"男"),function1);
        //类::new
        //条件1 如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。
        Supplier supplier = ()->new Employee();
        Supplier supplier1 = Employee::new;
        supplier(supplier1);
        //思考:方法引用只能调用无参的构造方法吗?如何调用有参构造?
        //可以调用有参构造,调用时必须重写Supplier接口中的get方法,把属性当成get方法参数
        MySupplier mySupplier = (name,age,salary,gender)->new Employee(name,age,salary,gender);
        MySupplier mySupplier1 = Employee::new;
        String name = "LBJ";
        int age = 35;
        double salary = 25000;
        String gender = "男";
        mySupplier(name,age,salary,gender,mySupplier1);

    }

    public static boolean biPredicate(String str1,String str2,BiPredicate biPredicate){
        return biPredicate.test(str1,str2);
    }
    public static void comparator(int n1,int n2,Comparator comparator){
        int compare = comparator.compare(n1, n2);
        if(compare>0){
            System.out.println(n1+"大");
        }else if(compare==0){
            System.out.println("两个数相等");
        }else{
            System.out.println(n2+"大");
        }
    }

    public static void function(Employee employee,Function function){
        if(employee!=null){
            System.out.println(employee.getName().toString());
        }
    }
    //打印无参对象
    public static void supplier(Supplier supplier){
        Employee employee = supplier.get();
        if(employee!=null){
            System.out.println(employee);
        }
    }
    public static void mySupplier(String name,int age,double salary,String gender,MySupplier mySupplier){
        Employee employee = mySupplier.get(name, age, salary, gender);
        if(employee!=null){
            System.out.println(employee.toString());
        }
    }
}

Stream API

什么是Stream:

一个Stream表面上与一个集合很类似,集合中保存的是数据,而流中对数据的操作。

特点:
    Stream 自己不会存储元素。
    Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
    Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

    Stream遵循“做什么,而不是怎么去做”的原则。只需要描述需要做什么,而不用考虑程序是怎样实现的。

简单应用

如果想计算集合中字符串长度大于3的元素个数,传统方法:遍历,逐一判断是否满足条件

而用Stream API:将集合转成流进行操作,如:list.stream().filter(s->s.length()>3).count();就能很方便的计算出长度大于3的字符串个数。

public class Demo1 {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add("hello");list.add("world");list.add("hi");list.add("we");list.add("seeyou");
        //传统做法,遍历
        int count=0;
        for (String s : list) {
            if(s.length()>3){
                count++;
            }
        }
        System.out.println(count);
        //Stream API
        long count1 = list.stream()
                .filter(s -> s.length() > 3)
                .count();
        System.out.println(count1);
    }
}

使用Stream会有三个阶段:

  1. 创建一个Stream。 (创建)

  2. 在一个或多个步骤中,将初始Stream转化到另一个Stream的中间操作。 (中间操作)

  3. 使用一个终止操作来产生一个结果。该操作会强制他之前的延迟操作立即执行。在这之后,该Stream就不会在被使用了。(终止操作)

Stream的创建方法:以下4种

// 1. Stream.of方法
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays.of方法
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. 集合方法
List list = Arrays.asList(strArray);
stream = list.stream();
stream = list.parallelStream();//并行流

//4.创建无限流
//迭代
Stream stream = Stream.iterate(0, (x)->x+2);
stream.limit(10)
  .forEach(System.out::println);
//生成
Stream stream2 = Stream.generate(()->Math.random());
stream2.limit(5)
  .forEach(System.out::println);

以上四种方法中重点掌握集合方法创建流

Stream中间操作:

中间操作包括:map(映射) (mapToInt, flatMap 等)、 filter(过滤)、distinct(去重)、sorted、peek()、limit、skip、parallel、sequential、unordered。

         //filter---从流中排除元素
        // limit——截断流,使其元素不超过给定数量。
        // skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
        // distinct——筛选,通过流所生成元素的 equals() 去除重复元素

        // map——接收 Lambda ,
        // 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

        //sorted()——自然排序

       //sorted(Comparator com)--------定制排序

       // unsorted() 把比较的约束去掉,并不能真正打乱集合中元素的顺序,只是保证下次插入元素时不按之前规定的条件

参考代码:

​
public class Demo3 {
    public static void main(String[] args) {
        List employees=new ArrayList<>();
        employees.add(new Employee("xxx", 30, 10000));
        employees.add(new Employee("yyy", 29, 8000));
        employees.add(new Employee("zzz", 22, 12000));
        employees.add(new Employee("张三", 21, 20000));
        employees.add(new Employee("李四", 32, 22000));
        employees.add(new Employee("李四", 32, 24000));
        //employees.add(new Employee("李四", 32, 22000));
        //1 筛选和切片
        // filter---从流中排除元素
        // limit——截断流,使其元素不超过给定数量。
        // skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
        // distinct——筛选,通过流所生成元素的 equals() 去除重复元素
        System.out.println("------filter-----");
        employees.stream()
                .filter(e->e.getAge()>25)
                .forEach(System.out::println);
        System.out.println("------limit-----");
        employees.stream()
                .limit(2)
                .forEach(System.out::println);
        System.out.println("-------skip------");
        employees.stream()
                .skip(2)
                .limit(2)
                .forEach(System.out::println);
        System.out.println("-----distinct------");
        employees.stream()
                .distinct()
                .forEach(System.out::println);

        System.out.println("-------------map---------------");
        employees.stream()
                .map(e->e.getName())
                .forEach(System.out::println);

       List strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
        strList.stream()
                .map(s->s.toUpperCase())
                .forEach(System.out::println);


        System.out.println("-------排序 ---------");

        employees.stream().sorted((o1,o2)->{
            int n1=o1.getAge()-o2.getAge();
            int n2=Double.compare(o1.getSalary(), o2.getSalary());
            return n1==0?n2:n1;
        }).unordered()
        .forEach(System.out::println);
        System.out.println("----------");
        //unordered:把比较的约束给去掉
        employees.stream().unordered().forEach(System.out::println);


    }
}

​

 

 

Stream 的终止操作

终止操作包括:forEach(遍历)、forEachOrdered、toArray、reduce(规约)、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator。

参考代码:

public class Demo4 {

    public static void main(String[] args) {
        List employees=new ArrayList<>();
        employees.add(new Employee("xxx", 30, 10000,"男"));
        employees.add(new Employee("yyy", 29, 8000,"男"));
        employees.add(new Employee("zzz", 22, 12000,"男"));
        employees.add(new Employee("张三", 21, 20000,"男"));
        employees.add(new Employee("李四", 32, 22000,"妖"));

//        allMatch——检查是否匹配所有元素
//        anyMatch——检查是否至少匹配一个元素
//        noneMatch——检查是否没有匹配的元素
//        findFirst——返回第一个元素
//        findAny——返回当前流中的任意元素
//        count——返回流中元素的总个数
//        max——返回流中最大值
//        min——返回流中最小值

         boolean b=employees.stream()
                .allMatch(e->e.getGender().equals("男"));
        System.out.println("结果:"+b);

        boolean b2= employees.stream().anyMatch(e -> e.getGender().equals("妖"));
        System.out.println(b2);

        boolean b3= employees.stream().noneMatch(e -> e.getGender().equals("女"));
        System.out.println(b3);

        Optional first = employees.stream().findFirst();
        Employee employee = first.get();
        System.out.println(employee.toString());
        System.out.println("-------findAny()  当数据比较少的使用单线程一般都是第一个,并行流返回执行最快的线程的数据-------");
        Optional any = employees.parallelStream().findAny();
        System.out.println(any.get().toString());
        System.out.println("------max--------");
        Optional max = employees.stream().max((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary()));
        System.out.println(max.get().toString());


        System.out.println("--------reduce-----------------");

        List list= Arrays.asList(10,20,30,40);
        Integer sum = list.stream().reduce(10, (x, y) -> x + y);
        System.out.println(sum);

        System.out.println("计算所有员工的工资");
        Double salay = employees.stream()
                .map(Employee::getSalary)
                .reduce(0.0, Double::sum);
        System.out.println(salay);

        System.out.println("--------获取所有的员工姓名的集合--------");
        List collect = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        System.out.println(collect);

    }
}

并行操作:

Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

list.stream()

list.parallelStream()

当集合中元素非常庞大的时候parallelStream()的效率比stream()的效率高很多

新时间日期API

  • Local(本地) − 简化了日期时间的处理,没有时区的问题。

  • ZoneId (时区) − 通过定制的时区处理日期时间。

SimpleDateFormat是线程非安全的!!

java.util.Date 是非线程安全的,所有的日期类都是可变的(每次修改后data就会发生改变),这是Java日期类最大的问题之一。 

本地化日期时间API

LocalDate/LocalTime 和 LocalDateTime 类可以在处理时区不是必须的情况。

LocalDate只表示日期,LocalTime只表示时间,而LocalDateTime表示日期和时间(比较常用) 

基本用法:

LocalDateTime date1 = LocalDateTime.now();//创建当前时间

LocalDateTime date2 = LocalDateTime.of(year,month,day,hour,minute,second);//创建指定的时间

LocalDateTime newDate = date1.plusDay(int n)/plusMonth(int n)....//表示在date1的基础上加上指定数值,返回一个新日期,而不改变date1的值

LocalDateTime newDate = date1.minusDay(int n)/minusMonth(int n)//同上

日期和字符串之间的转换:DateTimeFormatter

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("YYYY-MM-DD HH:mm-SS");

LocalDateTime date = LocalDateTime.now();

String str = date.format(dtf);

Instant、ZoneId、Date----Instance----LocalDateTime(重点,看代码中时间转换)

Instant 时间戳 类似以前的Date、Timestamp,它是以Unix元年(传统 的设定为UTC时区1970年1月1日午夜时分)开始 所经历的描述进行运算

ZoneId 时区

public static void main(String[] args) throws Exception {
		//时间戳
		Instant instant=Instant.now();
		System.out.println(instant);
		Thread.sleep(1000);
		Instant instant2=Instant.now();
		
		long millis = Duration.between(instant, instant2).toMillis();
		System.out.println(millis);
		
		//时区
		System.out.println(ZoneId.getAvailableZoneIds());

		ZoneId zone1 = ZoneId.of("Europe/Berlin");
		ZoneId zone2 = ZoneId.of("Brazil/East");
		System.out.println(zone1.getRules());
		System.out.println(zone2.getRules());
  
  
  		 //时间转换
        //Date-----Instant-------LocalDateTime
        System.out.println("--------Date-----Instant-------LocalDateTime---------");
        Date date=new Date();
        //把date转成instant
        Instant instant1 = date.toInstant();
        //intant转成LocalDateTime
        LocalDateTime localDateTime = instant1.atZone(ZoneId.systemDefault()).toLocalDateTime();
        System.out.println(localDateTime);
        System.out.println("--------LocalDateTime-----Instant-------Date---------");

        Instant instant3 = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
        Date date2 = Date.from(instant3);
        System.out.println(date2);
	}

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Java第一阶段学习总结)