一、Lambda表达式
什么是Lambda表达式
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。
lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。
为什么要用?
lambda是一个匿名函数,我们可以把lambda理解为一个可以传递的代码(将代码像数据一样传递),可以写出更简洁更灵活的代码,增强了代码的可读性。
lambda 的结构
a) 一个 Lambda 表达式可以有零个或多个参数
b) 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
c) 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
d) 空圆括号代表参数集为空。例如:() -> 42
e) 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
f) Lambda 表达式的主体可包含零条或多条语句
g) 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
h) 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
方法引用
若lambda体中的内容有方法已经实现了,那么可以使用“方法引用” ,也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单
(a) 方法引用
三种表现形式:
- 对象::实例方法名
例如:Consumer<Integer> con = System.out::println;
- 类::静态方法名
例如:BiFunction<Integer, Integer, Integer> biFun = Integer::compare;
- 类::实例方法名 (lambda参数列表中第一个参数是实例方法的调用者,第二个参数是实例方法的参数时可用)
例如:BiFunction<String, String, Boolean> fun = String::equals;
(b)构造器引用
格式:ClassName::new
例如:Function<Integer, Employee> fun = Employee::new;
(c)数组引用
格式:Type[]::new
例如:Function<Integer, String[]> fun = String[]::new;
lambda使用举例:
1 /** 2 * 1.ArrayList的使用 3 */ 4 @Test 5 public void test1(){ 6 //传统方式 7 Integer[] numArr= {1,6,3,2,4,5,7}; 8 List<Integer> list = Arrays.asList(numArr); 9 for (Integer num:numArr){ 10 System.out.println("===>"+num); 11 } 12 13 //lambda方式 14 list.forEach((num)->{System.out.println("===>"+num); }); 15 System.out.println("-----------------------"); 16 17 } 18 19 /** 20 * 2.Runnable(线程)的使用 21 */ 22 @Test 23 public void test2(){ 24 //使用匿名内部类 25 new Runnable(){ 26 @Override 27 public void run() { 28 System.out.println("hello word!"); 29 } 30 }.run(); 31 //使用lambda表达式 32 Runnable runnable = ()-> System.out.println("hello word!"); 33 System.out.println("-----------------------"); 34 } 35 36 /** 37 * 3.Thread(线程的使用) 38 */ 39 @Test 40 public void test3(){ 41 //使用匿名内部类 42 new Thread(new Runnable(){ 43 @Override 44 public void run() { 45 System.out.println("hello word!"); 46 } 47 }).start(); 48 //使用lambda表达式 49 new Thread(()-> System.out.println("hello word")); 50 System.out.println("-----------------------"); 51 } 52 /** 53 * 4.sort排序 54 */ 55 @Test 56 public void test4(){ 57 //正常情况使用排序 58 String[] player = {"aa","cc","bb","ee","dd","gg","ff"}; 59 Arrays.sort(player, new Comparator<String>() { 60 @Override 61 public int compare(String o1, String o2) { 62 return o1.compareTo(o2); 63 } 64 public String compare1(String o1, String o2) { 65 return o1; 66 } 67 }); 68 Arrays.asList(player).forEach(e-> System.out.println("==>"+e)); 69 70 //lambda表达式 71 //方式1 72 Comparator<String> comparator = (o1,o2)-> ( 73 o1.compareTo(o2) 74 75 ); 76 Arrays.sort(player,comparator); 77 Arrays.asList(player).forEach(e-> System.out.println("==>"+e)); 78 //方式2 79 Arrays.sort(player,(String o1,String o2)->(o1.compareTo(o2))); 80 Arrays.asList(player).forEach(e-> System.out.println("==>"+e)); 81 }
二、Stream流
定义:
流是Java API的新成员,它允许我们以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,我们可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,也就是说我们不用写多线程代码了。
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。
Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。
对stream的操作可以分为两类,中间操作(intermediate operations)和结束操作(terminal operations):
中间操作总是会惰式执行,调用中间操作只会生成一个标记了该操作的新stream。
结束操作会触发实际计算,计算发生时会把所有中间操作积攒的操作以pipeline的方式执行,这样可以减少迭代次数。计算完成之后stream就会失效。
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
Stream操作的三个步骤
创建stream:一个数据源(如:集合、数组),获取一个流
中间操作(过滤、map):一个中间操作链,对数据源的数据进行处理
终止操作:一个终止操作,执行中间操作链,并产生结果
stream的创建:
// 1,校验通过Collection 系列集合提供的stream()或者paralleStream()
List<String> list = new ArrayList<>();
Strean<String> stream1 = list.stream();
// 2.通过Arrays的静态方法stream()获取数组流
String[] str = new String[10];
Stream<String> stream2 = Arrays.stream(str);
// 3.通过Stream类中的静态方法of
Stream<String> stream3 = Stream.of("aa","bb","cc");
Stream的中间操作:
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
Stream的终止操作:
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
stream使用测试
1 /** 2 * Stream操作 3 */ 4 @Test 5 public void test5(){ 6 /** 7 * Stream创建 8 */ 9 String[] arr = {"1","3","2","4"}; 10 //通过Stream类中的静态方法of 11 Stream stream1 = Stream.of("1","3","2","4"); 12 //通过Arrays的静态方法stream()获取数组流 13 Stream stream2 = Arrays.stream(arr); 14 //通过Collection 系列集合提供的stream() 15 Stream stream3 = Arrays.asList(arr).stream(); 19 } 22 23 @Test public void testForEach(){ 24 List<User> list = Arrays.asList( 25 new User("张三", 11), 26 new User("王九", 20), 27 new User("王五", 91), 28 new User("张三", 8), 29 new User("李四", 44), 30 new User("陆二狗", 43), 31 new User("刘大麻子", 49) ); 32 /** 33 * forEach 迭代输出每条数据. 34 */ 35 // java 8 前 36 System.out.println("java 8 前"); 37 for(User user: list){ 38 System.out.println(user); 39 } 40 // java 8 lambda 41 System.out.println("java 8 lambda"); 42 list.forEach(user -> System.out.println(user)); 43 // java 8 stream lambda 44 System.out.println("java 8 stream lambda"); 45 list.stream().forEach(user -> System.out.println(user)); 46 System.out.println("-------------------"); 47 //筛选过滤去重 48 list.stream().filter(e->e.getAge()>11).limit(3).forEach(user -> System.out.println(user)); 49 } 50 /** * sort 排序. */ 51 @Test public void testSort() { 52 List<User> list = Arrays.asList( 53 new User("张三", 11), 54 new User("王九", 20), 55 new User("王五", 91), 56 new User("张三", 8), 57 new User("李四", 44), 58 new User("陆二狗", 43), 59 new User("刘大麻子", 49) ); 60 System.out.println("-----排序前-----"); 61 list.forEach(user -> System.out.println(user)); 62 System.out.println("-----排序后-----"); 63 // java 8 前 64 System.out.println("java 8 前"); 65 Collections.sort(list, new Comparator<User>() { 66 @Override 67 public int compare(User o1, User o2) { 68 return String.valueOf(o1.getAge()).compareTo(String.valueOf(o2.getAge())); 69 } 70 }); 71 for (User user : list) { 72 System.out.println(user); 73 } 74 //java 8 stream 方法引用 75 System.out.println("java 8 stream 方法引用"); 76 list.stream().sorted(Comparator.comparing(user -> user.getAge())). 77 forEach(user -> System.out.println(user)); 78 } 79 80 /** * filter 过滤. */ 81 @Test public void testFilter() { 82 List<User> list = Arrays.asList( 83 new User("张三", 11), 84 new User("王九", 20), 85 new User("王五", 91), 86 new User("张三", 8), 87 new User("李四", 44), 88 new User("陆二狗", 43), 89 new User("刘大麻子", 49) ); 90 System.out.println("-----排序前-----"); 91 // 输出年龄大于50的人 92 System.out.println("-----过滤前-----"); 93 list.forEach(user -> System.out.println(user)); 94 System.out.println("-----过滤后-----"); 95 //java 8 前 96 System.out.println("java 8 前"); 97 for(User user: list){ 98 if (user.getAge() > 50) { 99 System.out.println(user); 100 } 101 } 102 //java 8 stream 103 System.out.println("java 8 stream"); 104 list.stream().filter((User user) -> user.getAge() > 50). 105 forEach(user -> System.out.println(user)); 106 } 107 /** * map 映射. */ 108 @Test public void testMap() { 109 List<User> list = Arrays.asList( 110 new User("张三", 11), 111 new User("王九", 20), 112 new User("王五", 91), 113 new User("张三", 8), 114 new User("李四", 44), 115 new User("陆二狗", 43), 116 new User("刘大麻子", 49) ); 117 // 只输出所有人的年龄 118 list.stream().forEach(user -> System.out.println(user)); 119 System.out.println("映射后------>"); 120 121 List<Integer> ages = list.stream().map(user -> user.getAge()).collect(Collectors.toList()); 122 ages.forEach(age -> System.out.println(age)); 123 // 小写转大写 124 List<String> words = Arrays.asList("aaa", "vvvv", "cccc"); 125 System.out.println("全部大写---->"); 126 List<String> collect = words.stream().map(s -> s.toUpperCase()).collect(Collectors.toList()); 127 collect.forEach(s -> System.out.println(s)); 128 }
三、新的日期API
1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time包下。比原来实用了很多。
新的日期API好处:
* 之前使用的java.util.Date月份从0开始,我们一般会+1使用,很不方便,java.time.LocalDate月份和星期都改成了enum
* java.util.Date和SimpleDateFormat都不是线程安全的,而LocalDate和LocalTime和最基本的String一样,是不变类型,不但线程安全,而且不能修改。
* java.util.Date是一个“万能接口”,它包含日期、时间,还有毫秒数,更加明确需求取舍
* 新接口更好用的原因是考虑到了日期时间的操作,经常发生往前推或往后推几天的情况。用java.util.Date配合Calendar要写好多代码,而且一般的开发人员还不一定能写对
LocalDate/LocalTime/LocalDateTime
LocalDate为日期处理类、LocalTime为时间处理类、LocalDateTime为日期时间处理类,方法都类似,具体可以看API文档或源码,选取几个代表性的方法做下介绍。
now相关的方法可以获取当前日期或时间,of方法可以创建对应的日期或时间,parse方法可以解析日期或时间,get方法可以获取日期或时间信息,with方法可以设置日期或时间信息,plus或minus方法可以增减日期或时间信息;
TemporalAdjusters
这个类在日期调整时非常有用,比如得到当月的第一天、最后一天,当年的第一天、最后一天,下一周或前一周的某天等。
DateTimeFormatter
以前日期格式化一般用SimpleDateFormat类,但是不怎么好用,现在1.8引入了DateTimeFormatter类,默认定义了很多常量格式(ISO打头的),在使用的时候一般配合LocalDate/LocalTime/LocalDateTime使用,比如想把当前日期格式化成yyyy-MM-dd hh:mm:ss的形式:
1 /** 2 * 时间处理api 3 */ 4 @Test 5 public void testDate(){ 6 //当前时间 7 LocalDate now = LocalDate.now(); 8 System.out.println("当前时间:"+now); 9 10 //前一天时间 11 LocalDate yesterday = now.minusDays(1); 12 System.out.println("昨天:"+yesterday); 13 //上个月 14 LocalDate lastMonth = now.minusMonths(1); 15 System.out.println("上个月:"+lastMonth); 16 //上一周 17 LocalDate lastWeek = now.minusWeeks(1); 18 System.out.println("上一周:"+lastWeek); 19 //一周后 20 LocalDate nextWeek = now.plusWeeks(1); 21 System.out.println("一周后:"+nextWeek); 22 //指定日期 23 LocalDate ofDate = LocalDate.of(2020, 4, 7); 24 System.out.println("指定日期:"+ofDate); 25 //转换日期对象 26 LocalDate parseDate = LocalDate.parse("2020-04-09"); 27 System.out.println("转换日期对象:"+parseDate); 28 // 取本月第1天: 29 LocalDate firstDayOfThisMonth = now.with(TemporalAdjusters.firstDayOfMonth()); 30 LocalDate firstDayOfThisMonth1 = now.withDayOfMonth(1); 31 System.out.println("本月第一天:"+firstDayOfThisMonth); 32 System.out.println("本月第一天1:"+firstDayOfThisMonth1); 33 // 取本月第2天: 34 LocalDate secondDayOfThisMonth = now.withDayOfMonth(2); 35 System.out.println("本月第二天:"+secondDayOfThisMonth); 36 // 取本月最后一天,再也不用计算是28,29,30还是31: 37 LocalDate lastDayOfThisMonth = now.with(TemporalAdjusters.lastDayOfMonth()); 38 System.out.println("本月最后一天:"+lastDayOfThisMonth); 39 //获取当前月份,得到结果不用加1 40 int month = now.getMonthValue(); 41 System.out.println("当前月份:"+month); 42 int dayOfYear = now.getDayOfYear(); 43 System.out.println("2020年已经度过的天数:"+dayOfYear); 44 LocalDateTime nowTime = LocalDateTime.now(); 45 String formatDate = DateTimeFormatter.ISO_LOCAL_DATE.format(nowTime); 46 System.out.println("formatDate:"+formatDate); 47 DateTimeFormatter formatter =DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss"); 48 String formatDateTime = formatter.format(nowTime); 49 System.out.println("formatDateTime:"+formatDateTime); 50 DateTimeFormatter formatter1 =DateTimeFormatter.ofPattern("YYYY/MM/dd HH:mm:ss"); 51 String formatDateTime1 = formatter1.format(nowTime); 52 System.out.println("formatDateTime1:"+formatDateTime1); 53 }
运行结果: