Java 【JDK8特性】【lambda表达式、函数式接口、Optional、LocalDate、LocalTime、LocalDateTime、运算语法】
1、Lambda表达式
java8中一个非常重要的特性就是lambda表达式,我们可以把它看成是一种闭包,它允许把函数当做参数来使用,是面向函数式编程的思想,一定程度上可以使代码看起来更加简洁。例如以前我们使用匿名内部类来实现代码:
1 //匿名内部类写法
2 new Thread(new Runnable() {
3 @Override
4 public void run() {
5 System.out.println("内部类写法");
6 }
7 }).start();
使用lambda则更加简洁:
1 //lambda 写法
2 new Thread(() -> System.out.println("lambda写法")).start();
lambda表达式语法
1 (paramters) -> expression;
或者
1 (paramters) -> {statements;}
2 展开如:
3 (Type1 param1, Type2 param2, Type2 param2, ...) -> {
4 statement1;
5 statement2;
6 statement3;
7 ...
8 return statementX;
9 }
lambda表达式特征
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
示例:
1 //入参为空
2 TestDemo no_param = () -> "hi, no param";
3 TestDemo no_param2 = () -> { return "hi, no param"; };
4 System.out.println(no_param.hi());
5
6 //单个参数
7 TestDemo2 param = name -> name;
8 TestDemo2 param2 = name -> { return name;};
9 System.out.println(param.hei("hei, grils"));
10
11 //多个参数
12 TestDemo3 multiple = (String hello, String name) -> hello + " " + name;
13 //一条返回语句,可以省略大括号和return
14 TestDemo3 multiple2 = (hello, name) -> hello + name;
15 //多条处理语句,需要大括号和return
16 TestDemo3 multiple3 = (hello, name) -> {
17 System.out.println("进入内部");
18 return hello + name;
19 };
20 System.out.println(multiple.greet("hello", "lambda"));
方法引用
在使用lambda时,如果方法体中只有一个方法的调用(包含构造方法),则可以使用方法引用进一步简化有以下几种类型
☆ 对象::实例方法,将lambda的参数当做方法的参数使用
objectName::instanceMethod
1 Consumer<String> sc = System.out::println;
2 //等效
3 Consumer<String> sc2 = (x) -> System.out.println(x);
4 sc.accept("618, 狂欢happy");
1 public class Test { 2 3 public static void main(String[] args) { 4 List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22); 5 StringBuilder sb = new StringBuilder(); // 创建实例 6 // map方法中使用的是 引用类的静态方法 -> 类名::静态方法名 7 list.stream().map(String::valueOf).forEach(new Consumer<String>() { 8 @Override 9 public void accept(String s) { 10 sb.append(s); 11 } 12 }); 13 14 // forEach方法中使用 引用对象的实例方法 -> 对象名::实例方法名 15 list.stream().map(String::valueOf).forEach(sb::append); 16 } 17 }
☆ 类::静态方法,将lambda的参数当做方法的参数使用
如果在重写方法的时候,方法体中只有一行代码,并且这行代码调用了某个类的静态方法,我们把要重写的抽象方法中所有的参数都按照顺序传入这个静态方法中,这个时候可以引用类的静态方法
ClassName::staticMethod // 类名::静态方法名
1 //ClassName::staticMethod 类的静态方法:把表达式的参数值作为staticMethod方法的参数
2
3 Function<Integer, String> sf = String::valueOf;
4 //等效
5 Function<Integer, String> sf2 = (x) -> String.valueOf(x);
6 String apply1 = sf.apply(61888);
1 public class Test { 2 3 public static void main(String[] args) { 4 List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22); 5 // 匿名内部类写法 6 list.stream().map(new Function<Integer, String>() { 7 @Override 8 public String apply(Integer x) { 9 return String.valueOf(x); 10 } 11 }); 12 // lambda简写 13 list.stream().map(x -> String.valueOf(x)); 14 // 引用类的静态方法简写 15 list.stream().map(String::valueOf); 16 } 17 }
☆ 类::实例方法,将lambda的第一个参数当做方法的调用者,其他的参数作为方法的参数。开发中尽量少些此类写法,减少后续维护成本。
ClassName::instanceMethod
示例:
1 //ClassName::instanceMethod 类的实例方法:把表达式的第一个参数当成instanceMethod的调用者,其他参数作为该方法的参数
2
3 BiPredicate<String, String> sbp = String::equals;
4 //等效
5 BiPredicate<String, String> sbp2 = (x, y) -> x.equals(y);
6 boolean test = sbp.test("a", "A");
构造函数
无参的构造方法就是类::实例方法模型,如:
1 Supplier<User> us = User::new;
2 //等效
3 Supplier<User> us2 = () -> new User();
4 //获取对象
5 User user = us.get();
当有参数时:
1 //一个参数,参数类型不同则会编译出错
2 Function<Integer, User> uf = id -> new User(id);
3 //或加括号
4 Function<Integer, User> uf2 = (id) -> new User(id);
5 //等效
6 Function<Integer, User> uf3 = (Integer id) -> new User(id);
7 User apply = uf.apply(61888);
8
9 //两个参数
10 BiFunction<Integer, String, User> ubf = (id, name) -> new User(id, name);
11 User 狂欢happy = ubf.apply(618, "狂欢happy");
继承及实现具有相同默认方法的父类或接口问题
接口A:
1 public interface A {
2
3 String hi();
4
5 String greet();
6
7 default void hello() {
8 System.out.println("A.hello");
9 }
10
11 }
接口B:
1 public interface B {
2
3 String hi();
4
5 String hh();
6
7 default void hello() {
8 System.out.println("B.hello");
9 }
10
11 }
类C实现A,B:
1 public class C implements A, B{
2
3 @Override
4 public String hi() {
5 return "C.hi";
6 }
7
8 @Override
9 public String greet() {
10 return "C.greet";
11 }
12
13 @Override
14 public String hh() {
15 return "C.hh";
16 }
17
18 /**
19 * 子类优先继承父类的方法, 如果父类没有相同签名的方法,才继承接口的默认方法。
20 * 编译报错解决1:覆盖法
21 */
22 @Override
23 public void hello() {
24 System.out.println("C.hello");
25 }
26
27 /**
28 * 编译报错解决2:指定实现的父接口
29 */
30 // @Override
31 // public void hello() {
32 // A.super.hello();
33 // B.super.hello();
34 // }
35
36 }
进一步测试继承具有相同方法的父类:
类D:
public class D {
public void hello() {
System.out.println("D.hello");
}
}
类C继承类D:
1 public class C extends D implements A, B{
2
3 @Override
4 public String hi() {
5 return "C.hi";
6 }
7
8 @Override
9 public String greet() {
10 return "C.greet";
11 }
12
13 @Override
14 public String hh() {
15 return "C.hh";
16 }
17
18 /**
19 * 子类优先继承父类的方法, 如果父类没有相同签名的方法,才继承接口的默认方法。
20 * 编译报错解决1:覆盖法
21 */
22 // @Override
23 // public void hello() {
24 // System.out.println("C.hello");
25 // }
26
27 /**
28 * 编译报错解决2:指定实现的父接口
29 */
30 // @Override
31 // public void hello() {
32 // A.super.hello();34 // }
36 }
此时若不覆盖或指定父接口的方法时,类C将继承类D的hello方法。
总结
java8引入lambda表达式是接收了函数式编程语言的思想,例如scala之类的,它将函数视为一等公民,可以使用高阶函数等。和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。
2、Optional
java8 推出的Optional的目的就是为了杜绝空指针异常,帮助开发者开发出更优雅的代码,使用Optional不正确时,将会违背设计者的初衷。
Optional的构造方式:
1、Optional.of(T) 该方式的入参不能为null,否则会有NPE,在确定入参不为空时使用该方式。如何正确使用Optional
尽量避免使用的地方:
1、避免使用Optional.isPresent()来检查实例是否存在,因为这种方式和null != obj没有区别,这样用就没什么意义了。正确使用方式:
1、实例对象存在则返回,否则提供默认值或者通过方法来设置返回值,即使用orElse/orElseGet方式:
1 // 存在则返回
2 User king = new User(1, "king");
3 Optional<User> userOpt = Optional.of(king);
4 User user = userOpt.orElse(null);
5 System.out.println(user.getName());
1 // 不存在提供默认值
2 User user2 = null;
3 Optional<User> userOpt2 = Optional.ofNullable(user2);
4 User user3 = userOpt2.orElse(new User("张三")); // 如果User为null, 则返回orElse中提供的数据
5 System.out.println(user3.getName());
1 // 通过方法提供值
2 User user4 = userOpt2.orElseGet(() -> new User(0, "DEFAULT"));
3 System.out.println(user4.getName())
不建议这样使用:
1 if(userOpt.isPresent()) {
2 System.out.println(userOpt.get().getName());
3 } else {
4 //。。。
5 }
2、使用ifPresent()来进行对象操作,存在则操作,否则不操作。
1 //实例存在则操作,否则不操作
2 userOpt.ifPresent(u -> System.out.println(u.getName()));
3 userOpt2.ifPresent(u -> System.out.println(u.getName()));
3、使用map/flatMap来获取关联数据
1 //使用map方法获取关联数据
2 System.out.println(userOpt.map(u -> u.getName()).orElse("Unknown"));
3 System.out.println(userOpt2.map(u -> u.getName()).orElse("Default"));
4 //使用flatMap方法获取关联数据
5 List<String> interests = new ArrayList<String>();
6 interests.add("a");interests.add("b");interests.add("c");
7 user.setInterests(interests);
8 List<String> interests2 = Optional.of(user)
9 .flatMap(u -> Optional.ofNullable(u.getInterests()))
10 .orElse(Collections.emptyList());
11 System.out.println(interests2.isEmpty());
总结
正确使用Optiona不仅可以避免NPE问题,还可以使代码看起来更加简洁。
① 创建Optional
1 public class Test { 2 3 public static void main(String[] args) throws Exception { 4 // 创建optional对象 5 User user = getUser(); 6 // 通过Optional中的方法对对象进行包装 7 Optional<User> optional = Optional.ofNullable(user); 8 // 如果这个User对象不为空, 则会执行匿名内部类中的方法, 如果对象为空, 则不会执行 9 optional.ifPresent(new Consumer<User>() { 10 @Override 11 public void accept(User user) { 12 System.out.println(user.getName()); 13 } 14 }); 15 16 } 17 18 public static User getUser() { 19 return new User(1, "张三"); 20 } 21 } 22 23 @Data 24 @AllArgsConstructor 25 class User { 26 private Integer id; 27 private String name; 28 }
1 public class Test { 2 3 public static void main(String[] args) { 4 // 创建optional对象 5 Optional<User> optional = getUser(); 6 optional.ifPresent(x -> System.out.println(x.getName())); 7 } 8 9 public static Optional<User> getUser() { 10 return Optional.ofNullable(new User(1, "张三")); 11 } 12 } 13 14 @Data 15 @AllArgsConstructor 16 class User { 17 private Integer id; 18 private String name; 19 }
☆ 如果确定一个对象不是空的,则可以使用Optional的静态方法of()来把数据进行封装成Optional对象
1 public class Test { 2 3 public static void main(String[] args) { 5 User user = new User(1, "张三"); 6 Optional<User> optional = Optional.of(user);// 明确了对象不可能为空, 使用of方法是没问题的, 但是一旦对象为空, 则会报错, 因为of调用的Optional构造方法中校验为空就会抛空指针异常 7 optional.ifPresent(x-> System.out.println(x.getName())); 8 } 9 } 10 11 @Data 12 @AllArgsConstructor 13 class User { 14 private Integer id; 15 private String name; 16 }
☆ Optional的构造方法
1 private Optional(T var1) { 2 this.value = Objects.requireNonNull(var1); 3 } 4 // Objects.requireNonNull源码部分 5 public static <T> T requireNonNull(T var0) { 6 if (var0 == null) { 7 throw new NullPointerException(); 8 } else { 9 return var0; 10 } 11 }
☆ 如果一个方法的返回值类型为Optional类型,并且经过判断发现返回值为null,这个时候需要把null封装成Optional对象返回,则可以使用Optional的静态方法empty()来进行封装
1 public class Test { 2 3 public static void main(String[] args) { 4 Optional<User> userById = getUserById(2); 5 // 当Optional中的值为null的时候是不会执行ifPresent内部的方法的 6 userById.ifPresent(x -> System.out.println(x.getName())); 7 System.out.println("结束"); 8 } 9 10 public static Optional<User> getUserById(Integer id) { 11 User user = new User(1, "张三"); 12 if (user.getId().equals(id)) { 13 return Optional.of(user); 14 } 15 return Optional.empty(); 16 } 17 } 18 19 @Data 20 @AllArgsConstructor 21 class User { 22 private Integer id; 23 private String name; 24 }
② 安全消费值
调用Optional的ifPresent方法, 优雅的避免空指针异常,安全的消费数据
1 public class Test { 2 3 public static void main(String[] args) { 4 Optional<User> userById = getUserById(1); 5 // ifPresent方法内部判断了当对象不为空才会执行消费操作, 保证了消费的安全 6 userById.ifPresent(new Consumer<User>() { 7 @Override 8 public void accept(User user) { 9 System.out.println(user.getName()); 10 } 11 }); 12 } 13 14 public static Optional<User> getUserById(Integer id) { 15 User user = new User(1, "张三"); 16 if (user.getId().equals(id)) { 17 return Optional.of(user); 18 } 19 return Optional.empty(); 20 } 21 } 22 23 @Data 24 @AllArgsConstructor 25 class User { 26 private Integer id; 27 private String name; 28 }
③ 获取值
1 public class Test { 2 3 public static void main(String[] args) { 4 Optional<User> optional = getUserById(1); 5 // 使用get获取值的时候, 如果值为null, 则会直接抛异常 6 User user = optional.get(); 7 System.out.println(user); 8 } 9 10 public static Optional<User> getUserById(Integer id) { 11 User user = new User(1, "张三"); 12 if (user.getId().equals(id)) { 13 return Optional.of(user); 14 } 15 return Optional.empty(); 16 } 17 } 18 19 @Data 20 @AllArgsConstructor 21 class User { 22 private Integer id; 23 private String name; 24 }
get方法的源码
1 public T get() { 2 if (this.value == null) { 3 throw new NoSuchElementException("No value present"); // 当对象为null 直接抛异常 4 } else { 5 return this.value; 6 } 7 }
④ 安全的获取值
orElseGet
源码:
1 public T orElseGet(Supplier<? extends T> var1) { 2 return this.value != null ? this.value : var1.get(); 3 }
示例:
1 public class Test { 2 3 public static void main(String[] args) { 4 Optional<User> optional = getUserById(1); 5 // 如果Optional中的值不为null, 则返回该值, 如果为null, 则返回Supplier中get方法提供的默认值 6 User user = optional.orElseGet(new Supplier<User>() { 7 @Override 8 public User get() { 9 return new User(2, "李四"); 10 } 11 }); 12 } 13 14 public static Optional<User> getUserById(Integer id) { 15 User user = new User(1, "张三"); 16 if (user.getId().equals(id)) { 17 return Optional.of(user); 18 } 19 return Optional.empty(); 20 } 21 } 22 23 @Data 24 @AllArgsConstructor 25 class User { 26 private Integer id; 27 private String name; 28 }
orElseThrow
源码:
1 public <X extends Throwable> T orElseThrow(Supplier<? extends X> var1) throws X { 2 if (this.value != null) { 3 return this.value; 4 } else { 5 throw (Throwable)var1.get(); 6 } 7 }
示例:
1 public class Test { 2 3 public static void main(String[] args) { 4 Optional<User> optional = getUserById(1); 5 // 如果Optional中的值不为null, 则返回该值, 如果为null, 则抛出指定的异常 6 try { 7 User user = optional.orElseThrow(new Supplier<Throwable>() { 8 @Override 9 public Throwable get() { 10 return new RuntimeException("数据为null"); 11 } 12 }); 13 System.out.println(user.getName()); 14 } catch (Throwable e) { 15 e.printStackTrace(); 16 } 17 } 18 19 public static Optional<User> getUserById(Integer id) { 20 User user = new User(1, "张三"); 21 if (user.getId().equals(id)) { 22 return Optional.of(user); 23 } 24 return Optional.empty(); 25 } 26 } 27 28 @Data 29 @AllArgsConstructor 30 class User { 31 private Integer id; 32 private String name; 33 }
⑤ 过滤
使用filter方法对数据进行过滤,如果原本有数据,但是不符合判断,也会编程一个无数据的Optional对象
1 public class Test { 2 3 public static void main(String[] args) { 4 Optional<User> optional = getUserById(1); 5 // 当optional中存在值, 则执行filter中的条件判断, 当有满足的条件, 则返回该值 6 // 当optional中存在值, 则执行filter中的条件判断, 当没有有满足的条件, 则返回Optional.empty() 7 // 当optional中不存在值, 则不会执行filter中的条件判断, 且会返回Optional.empty() 8 Optional<User> userOptional = optional.filter(new Predicate<User>() { 9 @Override 10 public boolean test(User user) { 11 return user.getName().equals("李四"); 12 } 13 }); 14 // 在经过过滤之后, 也会返回空的值, 所以获取数据的时候也需要进行安全的获取值 15 User user = userOptional.orElseGet(()-> new User(2, "李四")); 16 } 17 18 public static Optional<User> getUserById(Integer id) { 19 User user = new User(1, "张三"); 20 if (user.getId().equals(id)) { 21 return Optional.of(user); 22 } 23 return Optional.empty(); 24 } 25 } 26 27 @Data 28 @AllArgsConstructor 29 class User { 30 private Integer id; 31 private String name; 32 }
⑥ 判断
在使用isPresent方法进行是否存在数据的判断,如果为空返回值为false,如果不为空,返回值为true,但是这种方式并不能体现Optional的好处,更推荐使用ifPresent方法
1 public class Test { 2 3 public static void main(String[] args) { 4 Optional<User> optional = getUserById(1); 5 // 判断Optional中的值不为null 6 if (optional.isPresent()) { 7 System.out.println(optional.get()); 8 } 9 } 10 11 public static Optional<User> getUserById(Integer id) { 12 User user = new User(1, "张三"); 13 if (user.getId().equals(id)) { 14 return Optional.of(user); 15 } 16 return Optional.empty(); 17 } 18 } 19 20 @Data 21 @AllArgsConstructor 22 class User { 23 private Integer id; 24 private String name; 25 }
⑦ 数据转换
Optional还提供了map方法可以让我们对数据进行转换,并且转换得到的数据也还是被Optional包装好的,保证使用的安全
1 public class Test { 2 3 public static void main(String[] args) { 4 Optional<User> optional = getUserById(1); 5 // 将Optional中的User转换成id 6 Optional<Integer> idOptional = optional.map(new Function<User, Integer>() { 7 @Override 8 public Integer apply(User user) { 9 return user.getId(); 10 } 11 }); 12 // 如果这个Optional中的值不为null, 则打印 13 idOptional.ifPresent(System.out::println); 14 } 15 16 public static Optional<User> getUserById(Integer id) { 17 User user = new User(1, "张三"); 18 if (user.getId().equals(id)) { 19 return Optional.of(user); 20 } 21 return Optional.empty(); 22 } 23 } 24 25 @Data 26 @AllArgsConstructor 27 class User { 28 private Integer id; 29 private String name; 30 }
3、日期时间
-
- Instant:表示时刻,不直接对应年月日信息,需要通过时区转换
-
- LocalDateTime: 表示与时区无关的日期和时间信息,不直接对应时刻,需要通过时区转换
- LocalDate:表示与时区无关的日期,与LocalDateTime相比,只有日期信息,没有时间信息
- LocalTime:表示与时区无关的时间,与LocalDateTime相比,只有时间信息,没有日期信息
- ZonedDateTime: 表示特定时区的日期和时间
- ZoneId/ZoneOffset:表示时区
- Instant:表示时刻,不直接对应年月日信息,需要通过时区转换
LocalDate
1 LocalDate now = LocalDate.now();
2 LocalDate now1 = LocalDate.now(Clock.systemUTC());
3
4 System.out.println(now);
5 System.out.println(now1);
6
7 LocalDate of = LocalDate.of(2019, 3, 6);
8 //严格按照ISO yyyy-MM-dd验证,03写成3都不行
9 LocalDate parse = LocalDate.parse("2019-03-06");
10 System.out.println(of);
11 System.out.println(parse);
12 14
15 //当天开始时间
16 System.out.println(now.atStartOfDay()); // 2024-03-19T00:00
17 //当月第一天日期
18 System.out.println(now.with(TemporalAdjusters.firstDayOfMonth()));
19 //本月第二天日期
20 System.out.println(now.withDayOfMonth(2));
21 //当月最后一天
22 System.out.println(now.with(TemporalAdjusters.lastDayOfMonth()));
23 System.out.println(now.getDayOfMonth());
24 //当月下一天
25 System.out.println(now.plusDays(1));
26 //当月上一天
27 System.out.println(now.minusDays(1));
28 System.out.println(now.getDayOfWeek());
29 //当月下一周
30 System.out.println(now.plusWeeks(1));
31 //当月上一周
32 System.out.println(now.minusWeeks(1));
33 System.out.println(now.getMonth() + "-" + now.getMonthValue());
34 //当月下一个月
35 System.out.println(now.plusMonths(1));
36 //当月上一个月
37 System.out.println(now.minusMonths(1));
38
39 //时间比较
40 System.out.println(now.isEqual(LocalDate.of(2019, 03, 06)));
LocalTime
1 LocalTime now = LocalTime.now(); 2 //指定时区 3 LocalTime now1 = LocalTime.now(Clock.system(ZoneId.systemDefault())); 4 LocalTime now2 = LocalTime.now(Clock.systemUTC()); 5 6 System.out.println(now); 7 System.out.println(now1); 8 System.out.println(now2); 9 10 System.out.println("************now************"); 11 //清除毫秒位 12 System.out.println(now.withNano(0)); 13 //获取当前的小时 14 System.out.println(now.getHour()); 15 //解析时间时间也是按照ISO格式识别,但可以识别以下3种格式: 12:00 12:01:02 12:01:02.345 16 System.out.println(LocalTime.parse("11:58:12")); 17 18 //时间比较 19 LocalTime other = LocalTime.of(13, 45, 59); 20 System.out.println(now.isBefore(other)); 21 System.out.println(now.isAfter(other));
LocalDateTime
1 LocalDateTime now = LocalDateTime.now(); 2 LocalDateTime now1 = LocalDateTime.now(Clock.system(ZoneId.systemDefault())); 3 LocalDateTime now2 = LocalDateTime.now(Clock.systemUTC()); 4 5 System.out.println(now); 6 System.out.println(now1); 7 System.out.println(now2); 8 // 时间格式转换 9 System.out.println(now.format(DateTimeFormatter.ofPattern(PATTERN_1))); 10 System.out.println(now.format(DateTimeFormatter.ofPattern(PATTERN_2))); 11 System.out.println(now.format(DateTimeFormatter.ofPattern(PATTERN_3)));
Date转换为LocalDateTime
1 /** 2 * Date转换为LocalDateTime 3 */ 4 public static LocalDateTime date2LocalDateTime() { 5 Date date = new Date(); 6 return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); 7 }
LocalDateTime转换为Date
1 /** 2 * LocalDateTime转换为Date 3 */ 4 public static Date localDateTime2Date() { 5 LocalDateTime now = LocalDateTime.now(); 6 Instant instant = now.atZone(ZoneId.systemDefault()).toInstant(); 7 return Date.from(instant); 8 }
日期时间比较
1 比如在15:30:30之前: 2 LocalTime.now().isBefore(LocalTime.of(15, 30,30)) 3 4 或15:30:30之后: 5 LocalTime.now().isAfter(LocalTime.of(15, 30,30)) 6 7 相等: 8 LocalTime.now().equal(LocalTime.of(15, 30,30))
LocalDateTime、LocalDate、LocalTime示例
1 import java.time.LocalDate; 2 import java.time.LocalDateTime; 3 import java.time.LocalTime; 4 import java.time.temporal.ChronoField; 5 import java.time.temporal.TemporalAdjusters; 6 7 public class Test { 8 9 public static void main(String[] args) { 10 LocalTime localTime = LocalTime.now(); // 时分秒 11 LocalDate localDate = LocalDate.now(); // 年月日 12 LocalDateTime localDateTime = LocalDateTime.now(); // 年月日时分秒 13 System.out.println(localTime); // 14:36:30.402 14 System.out.println(localDate); // 2024-03-19 15 System.out.println(localDateTime); // 2024-03-19T14:36:30.402 16 17 // 获取当月第一天 18 System.out.println(localDate.withDayOfMonth(1)); // 2024-03-01 19 System.out.println(localDate.with(TemporalAdjusters.firstDayOfMonth())); // 2024-03-01 20 System.out.println(localDate.with(ChronoField.DAY_OF_MONTH, 1L)); // 2024-03-01 21 22 LocalDate ofLocalDate = LocalDate.of(2020, 10, 20); 23 System.out.println(ofLocalDate); // 2020-10-20 24 LocalTime ofLocalTime = LocalTime.of(10, 20, 30); 25 System.out.println(ofLocalTime); // 10:20:30 26 LocalDateTime ofLocalDateTime = LocalDateTime.of(2020, 10, 20, 10, 30, 20); 27 System.out.println(ofLocalDateTime); // 2020-10-20T10:30:20 28 29 // 字符串解析 30 LocalDate parseLocalDate = LocalDate.parse("2022-03-03"); 31 System.out.println(parseLocalDate); // 2022-03-03 32 LocalTime parseLocalTime = LocalTime.parse("10:30:01"); 33 System.out.println(parseLocalTime); // 10:30:01 34 LocalDateTime parseLocalDateTime = LocalDateTime.parse("2022-03-03T10:30:01"); 35 System.out.println(parseLocalDateTime); // 2022-03-03T10:30:01 36 37 // 获取年、月、日 (LocalDate中没有时分秒, 只能获取年月日) 38 int year = localDate.getYear(); 39 int month = localDate.getMonth().getValue(); 40 int dayOfYear = localDate.getDayOfYear(); 41 int dayOfMonth = localDate.getDayOfMonth(); 42 int dayOfWeek = localDate.getDayOfWeek().getValue(); 43 System.out.println(year); // 2024 44 System.out.println(month); // 3 45 System.out.println(dayOfYear); // 79 (当年的第几天) 46 System.out.println(dayOfMonth); // 19 (当月的第几天, 几号) 47 System.out.println(dayOfWeek); // 2 (当周的第几天, 周几) 48 49 // 修改年月日, 会返回新的对象, 不修改原LocalDate 50 LocalDate withYear = localDate.withYear(2000); // 修改成2000年 51 LocalDate withMonth = localDate.withMonth(1); // 修改成1月 52 LocalDate withDayOfYear = localDate.withDayOfYear(78); // 修改成一年的第78天 53 LocalDate withDayOfMonth = localDate.withDayOfMonth(18); // 修改成一个月的第18天 54 System.out.println(withYear); // 2000-03-19 55 System.out.println(withMonth); // 2024-01-19 56 System.out.println(withDayOfYear); // 2024-03-18 57 System.out.println(withDayOfMonth); // 2024-03-18 58 59 // 年月日加减 60 LocalDate plusYears = localDate.plusYears(1); // 加1年 61 LocalDate plusMonths = localDate.plusMonths(1); // 加1个月 62 LocalDate plusDays = localDate.plusDays(1); // 加1天 63 LocalDate plusDaysOfWeek = localDate.plusDays(7); // 加7天 64 LocalDate plusWeeks = localDate.plusWeeks(1); // 加一周(7天) 65 LocalDate plusLocalDate = localDate.plusYears(1).plusMonths(1).plusDays(1); 66 System.out.println(plusYears); // 2025-03-19 67 System.out.println(plusMonths); // 2024-04-19 68 System.out.println(plusDays); // 2024-03-20 69 System.out.println(plusDaysOfWeek); // 2024-03-26 70 System.out.println(plusWeeks); // 2024-03-26 71 System.out.println(plusLocalDate); // 2025-04-20 72 System.out.println("==================================="); 73 74 LocalDate minusYears = localDate.minusYears(1); 75 LocalDate minusMonths = localDate.minusMonths(1); 76 LocalDate minusDays = localDate.minusDays(1); 77 LocalDate minusWeeks = localDate.minusWeeks(1); 78 LocalDate minusLocalDate = localDate.minusYears(1).minusMonths(1).minusDays(1); 79 System.out.println(minusYears); // 2023-03-19 80 System.out.println(minusMonths); // 2024-02-19 81 System.out.println(minusDays); // 2024-03-18 82 System.out.println(minusWeeks); // 2024-03-12 83 System.out.println(minusLocalDate); // 2023-02-18 84 System.out.println("==================================="); 85 86 87 // 获取时、分、秒 (LocalTime中没有年月日, 只能获取时分秒) 88 int hour = localTime.getHour(); 89 int minute = localTime.getMinute(); 90 int second = localTime.getSecond(); 91 int nano = localTime.getNano(); 92 System.out.println(hour); // 14 93 System.out.println(minute); // 36 94 System.out.println(second); // 30 95 System.out.println(nano); // 402000000 96 97 // 修改时分秒 98 LocalTime withHour = localTime.withHour(10); // 修改为10点 99 LocalTime withMinute = localTime.withMinute(20); // 修改分为20分 100 LocalTime withSecond = localTime.withSecond(30); // 修改秒为30秒 101 LocalTime withNano = localTime.withNano(0); // 修改纳秒为0 102 LocalTime withLocalTime = localTime.withHour(10).withMinute(20).withSecond(30).withNano(0); // 同时修改时分秒纳秒 103 System.out.println(withHour); // 10:36:30.402 104 System.out.println(withMinute); // 14:20:30.402 105 System.out.println(withSecond); // 14:36:30.402 106 System.out.println(withNano); // 14:36:30 107 System.out.println(withLocalTime); // 10:20:30 108 109 // 时分秒加 110 System.out.println(localTime); // 14:36:30.402 (当前时分秒) 111 System.out.println(localTime.plusHours(1)); // 15:36:30.402 (基于当前时分秒加1小时) 112 System.out.println(localTime.plusMinutes(1)); // 14:37:30.402 (基于当前时分秒加1分钟) 113 System.out.println(localTime.plusSeconds(1)); // 14:36:31.402 (基于当前时分秒加1秒) 114 System.out.println(localTime.plusNanos(1)); // 14:36:30.402000001 (基于当前时分秒加1纳秒) 115 System.out.println(localTime.plusHours(1).plusMinutes(1).plusSeconds(1).plusNanos(1)); // 15:37:31.402000001 (基于当前时分秒加1小时1分1秒1纳秒) 116 // 时分秒减 117 System.out.println(localTime.minusHours(1)); // 13:36:30.402 118 System.out.println(localTime.minusMinutes(1)); // 14:35:30.402 119 System.out.println(localTime.minusSeconds(1)); // 14:36:29.402 120 System.out.println(localTime.minusNanos(1)); // 14:36:30.401999999 121 System.out.println(localTime.minusHours(1).minusMinutes(1).minusSeconds(1).minusNanos(1)); // 13:35:29.401999999 122 } 123 }
ZoneId、ZonedDateTime时区
1 public static void main(String[] args) { 2 ZoneId zoneId = ZoneId.systemDefault(); 3 System.out.println(zoneId.getId()); // Asia/Shanghai 4 System.out.println(zoneId.toString()); // Asia/Shanghai 5 System.out.println(zoneId); // Asia/Shanghai 6 7 // 获取全部时区Id 8 System.out.println(ZoneId.getAvailableZoneIds()); 9 // [Asia/Aden, America/Cuiaba, Etc/GMT+9, Etc/GMT+8, Africa/Nairobi, America/Marigot, Asia/Aqtau, Pacific/Kwajalein, America/El_Salvador, Asia/Pontianak, Africa/Cairo, Pacific/Pago_Pago, Africa/Mbabane, Asia/Kuching, Pacific/Honolulu, Pacific/Rarotonga, America/Guatemala, Australia/Hobart, Europe/London, America/Belize, America/Panama, Asia/Chungking, America/Managua, America/Indiana/Petersburg, Asia/Yerevan, Europe/Brussels, GMT, Europe/Warsaw, America/Chicago, Asia/Kashgar, Chile/Continental, Pacific/Yap, CET, Etc/GMT-1, Etc/GMT-0, Europe/Jersey, America/Tegucigalpa, Etc/GMT-5, Europe/Istanbul, America/Eirunepe, Etc/GMT-4, America/Miquelon, Etc/GMT-3, Europe/Luxembourg, Etc/GMT-2, Etc/GMT-9, America/Argentina/Catamarca, Etc/GMT-8, Etc/GMT-7, Etc/GMT-6, Europe/Zaporozhye, Canada/Yukon, Canada/Atlantic, Atlantic/St_Helena, Australia/Tasmania, Libya, Europe/Guernsey, America/Grand_Turk, Asia/Samarkand, America/Argentina/Cordoba, Asia/Phnom_Penh, Africa/Kigali, Asia/Almaty, US/Alaska, Asia/Dubai, Europe/Isle_of_Man, America/Araguaina, Cuba, Asia/Novosibirsk, America/Argentina/Salta, Etc/GMT+3, Africa/Tunis, Etc/GMT+2, Etc/GMT+1, Pacific/Fakaofo, Africa/Tripoli, Etc/GMT+0, Israel, Africa/Banjul, Etc/GMT+7, Indian/Comoro, Etc/GMT+6, Etc/GMT+5, Etc/GMT+4, Pacific/Port_Moresby, US/Arizona, Antarctica/Syowa, Indian/Reunion, Pacific/Palau, Europe/Kaliningrad, America/Montevideo, Africa/Windhoek, Asia/Karachi, Africa/Mogadishu, Australia/Perth, Brazil/East, Etc/GMT, Asia/Chita, Pacific/Easter, Antarctica/Davis, Antarctica/McMurdo, Asia/Macao, America/Manaus, Africa/Freetown, Europe/Bucharest, Asia/Tomsk, America/Argentina/Mendoza, Asia/Macau, Europe/Malta, Mexico/BajaSur, Pacific/Tahiti, Africa/Asmera, Europe/Busingen, America/Argentina/Rio_Gallegos, Africa/Malabo, Europe/Skopje, America/Catamarca, America/Godthab, Europe/Sarajevo, Australia/ACT, GB-Eire, Africa/Lagos, America/Cordoba, Europe/Rome, Asia/Dacca, Indian/Mauritius, Pacific/Samoa, America/Regina, America/Fort_Wayne, America/Dawson_Creek, Africa/Algiers, Europe/Mariehamn, America/St_Johns, America/St_Thomas, Europe/Zurich, America/Anguilla, Asia/Dili, America/Denver, Africa/Bamako, Europe/Saratov, GB, Mexico/General, Pacific/Wallis, Europe/Gibraltar, Africa/Conakry, Africa/Lubumbashi, Asia/Istanbul, America/Havana, NZ-CHAT, Asia/Choibalsan, America/Porto_Acre, Asia/Omsk, Europe/Vaduz, US/Michigan, Asia/Dhaka, America/Barbados, Europe/Tiraspol, Atlantic/Cape_Verde, Asia/Yekaterinburg, America/Louisville, Pacific/Johnston, Pacific/Chatham, Europe/Ljubljana, America/Sao_Paulo, Asia/Jayapura, America/Curacao, Asia/Dushanbe, America/Guyana, America/Guayaquil, America/Martinique, Portugal, Europe/Berlin, Europe/Moscow, Europe/Chisinau, America/Puerto_Rico, America/Rankin_Inlet, Pacific/Ponape, Europe/Stockholm, Europe/Budapest, America/Argentina/Jujuy, Australia/Eucla, Asia/Shanghai, Universal, Europe/Zagreb, America/Port_of_Spain, Europe/Helsinki, Asia/Beirut, Asia/Tel_Aviv, Pacific/Bougainville, US/Central, Africa/Sao_Tome, Indian/Chagos, America/Cayenne, Asia/Yakutsk, Pacific/Galapagos, Australia/North, Europe/Paris, Africa/Ndjamena, Pacific/Fiji, America/Rainy_River, Indian/Maldives, Australia/Yancowinna, SystemV/AST4, Asia/Oral, America/Yellowknife, Pacific/Enderbury, America/Juneau, Australia/Victoria, America/Indiana/Vevay, Asia/Tashkent, Asia/Jakarta, Africa/Ceuta, Asia/Barnaul, America/Recife, America/Buenos_Aires, America/Noronha, America/Swift_Current, Australia/Adelaide, America/Metlakatla, Africa/Djibouti, America/Paramaribo, Asia/Qostanay, Europe/Simferopol, Europe/Sofia, Africa/Nouakchott, Europe/Prague, America/Indiana/Vincennes, Antarctica/Mawson, America/Kralendijk, Antarctica/Troll, Europe/Samara, Indian/Christmas, America/Antigua, Pacific/Gambier, America/Indianapolis, America/Inuvik, America/Iqaluit, Pacific/Funafuti, UTC, Antarctica/Macquarie, Canada/Pacific, America/Moncton, Africa/Gaborone, Pacific/Chuuk, Asia/Pyongyang, America/St_Vincent, Asia/Gaza, Etc/Universal, PST8PDT, Atlantic/Faeroe, Asia/Qyzylorda, Canada/Newfoundland, America/Kentucky/Louisville, America/Yakutat, America/Ciudad_Juarez, Asia/Ho_Chi_Minh, Antarctica/Casey, Europe/Copenhagen, Africa/Asmara, Atlantic/Azores, Europe/Vienna, ROK, Pacific/Pitcairn, America/Mazatlan, Australia/Queensland, Pacific/Nauru, Europe/Tirane, Asia/Kolkata, SystemV/MST7, Australia/Canberra, MET, Australia/Broken_Hill, Europe/Riga, America/Dominica, Africa/Abidjan, America/Mendoza, America/Santarem, Kwajalein, America/Asuncion, Asia/Ulan_Bator, NZ, America/Boise, Australia/Currie, EST5EDT, Pacific/Guam, Pacific/Wake, Atlantic/Bermuda, America/Costa_Rica, America/Dawson, Asia/Chongqing, Eire, Europe/Amsterdam, America/Indiana/Knox, America/North_Dakota/Beulah, Africa/Accra, Atlantic/Faroe, Mexico/BajaNorte, America/Maceio, Etc/UCT, Pacific/Apia, GMT0, America/Atka, Pacific/Niue, Australia/Lord_Howe, Europe/Dublin, Pacific/Truk, MST7MDT, America/Monterrey, America/Nassau, America/Jamaica, Asia/Bishkek, America/Atikokan, Atlantic/Stanley, Australia/NSW, US/Hawaii, SystemV/CST6, Indian/Mahe, Asia/Aqtobe, America/Sitka, Asia/Vladivostok, Africa/Libreville, Africa/Maputo, Zulu, America/Kentucky/Monticello, Africa/El_Aaiun, Africa/Ouagadougou, America/Coral_Harbour, Pacific/Marquesas, Brazil/West, America/Aruba, America/North_Dakota/Center, America/Cayman, Asia/Ulaanbaatar, Asia/Baghdad, Europe/San_Marino, America/Indiana/Tell_City, America/Tijuana, Pacific/Saipan, SystemV/YST9, Africa/Douala, America/Chihuahua, America/Ojinaga, Asia/Hovd, America/Anchorage, Chile/EasterIsland, America/Halifax, Antarctica/Rothera, America/Indiana/Indianapolis, US/Mountain, Asia/Damascus, America/Argentina/San_Luis, America/Santiago, Asia/Baku, America/Argentina/Ushuaia, Atlantic/Reykjavik, Africa/Brazzaville, Africa/Porto-Novo, America/La_Paz, Antarctica/DumontDUrville, Asia/Taipei, Antarctica/South_Pole, Asia/Manila, Asia/Bangkok, Africa/Dar_es_Salaam, Poland, Atlantic/Madeira, Antarctica/Palmer, America/Thunder_Bay, Africa/Addis_Ababa, Asia/Yangon, Europe/Uzhgorod, Brazil/DeNoronha, Asia/Ashkhabad, Etc/Zulu, America/Indiana/Marengo, America/Creston, America/Punta_Arenas, America/Mexico_City, Antarctica/Vostok, Asia/Jerusalem, Europe/Andorra, US/Samoa, PRC, Asia/Vientiane, Pacific/Kiritimati, America/Matamoros, America/Blanc-Sablon, Asia/Riyadh, Iceland, Pacific/Pohnpei, Asia/Ujung_Pandang, Atlantic/South_Georgia, Europe/Lisbon, Asia/Harbin, Europe/Oslo, Asia/Novokuznetsk, CST6CDT, Atlantic/Canary, America/Knox_IN, Asia/Kuwait, SystemV/HST10, Pacific/Efate, Africa/Lome, America/Bogota, America/Menominee, America/Adak, Pacific/Norfolk, Europe/Kirov, America/Resolute, Pacific/Kanton, Pacific/Tarawa, Africa/Kampala, Asia/Krasnoyarsk, Greenwich, SystemV/EST5, America/Edmonton, Europe/Podgorica, Australia/South, Canada/Central, Africa/Bujumbura, America/Santo_Domingo, US/Eastern, Europe/Minsk, Pacific/Auckland, Africa/Casablanca, America/Glace_Bay, Canada/Eastern, Asia/Qatar, Europe/Kiev, Singapore, Asia/Magadan, SystemV/PST8, America/Port-au-Prince, Europe/Belfast, America/St_Barthelemy, Asia/Ashgabat, Africa/Luanda, America/Nipigon, Atlantic/Jan_Mayen, Brazil/Acre, Asia/Muscat, Asia/Bahrain, Europe/Vilnius, America/Fortaleza, Etc/GMT0, US/East-Indiana, America/Hermosillo, America/Cancun, Africa/Maseru, Pacific/Kosrae, Africa/Kinshasa, Asia/Kathmandu, Asia/Seoul, Australia/Sydney, America/Lima, Australia/LHI, America/St_Lucia, Europe/Madrid, America/Bahia_Banderas, America/Montserrat, Asia/Brunei, America/Santa_Isabel, Canada/Mountain, America/Cambridge_Bay, Asia/Colombo, Australia/West, Indian/Antananarivo, Australia/Brisbane, Indian/Mayotte, US/Indiana-Starke, Asia/Urumqi, US/Aleutian, Europe/Volgograd, America/Lower_Princes, America/Vancouver, Africa/Blantyre, America/Rio_Branco, America/Danmarkshavn, America/Detroit, America/Thule, Africa/Lusaka, Asia/Hong_Kong, Iran, America/Argentina/La_Rioja, Africa/Dakar, SystemV/CST6CDT, America/Tortola, America/Porto_Velho, Asia/Sakhalin, Etc/GMT+10, America/Scoresbysund, Asia/Kamchatka, Asia/Thimbu, Africa/Harare, Etc/GMT+12, Etc/GMT+11, Navajo, America/Nome, Europe/Tallinn, Turkey, Africa/Khartoum, Africa/Johannesburg, Africa/Bangui, Europe/Belgrade, Jamaica, Africa/Bissau, Asia/Tehran, WET, Europe/Astrakhan, Africa/Juba, America/Campo_Grande, America/Belem, Etc/Greenwich, Asia/Saigon, America/Ensenada, Pacific/Midway, America/Jujuy, Africa/Timbuktu, America/Bahia, America/Goose_Bay, America/Virgin, America/Pangnirtung, Asia/Katmandu, America/Phoenix, Africa/Niamey, America/Whitehorse, Pacific/Noumea, Asia/Tbilisi, Europe/Kyiv, America/Montreal, Asia/Makassar, America/Argentina/San_Juan, Hongkong, UCT, Asia/Nicosia, America/Indiana/Winamac, SystemV/MST7MDT, America/Argentina/ComodRivadavia, America/Boa_Vista, America/Grenada, Asia/Atyrau, Australia/Darwin, Asia/Khandyga, Asia/Kuala_Lumpur, Asia/Famagusta, Asia/Thimphu, Asia/Rangoon, Europe/Bratislava, Asia/Calcutta, America/Argentina/Tucuman, Asia/Kabul, Indian/Cocos, Japan, Pacific/Tongatapu, America/New_York, Etc/GMT-12, Etc/GMT-11, America/Nuuk, Etc/GMT-10, SystemV/YST9YDT, Europe/Ulyanovsk, Etc/GMT-14, Etc/GMT-13, W-SU, America/Merida, EET, America/Rosario, Canada/Saskatchewan, America/St_Kitts, Arctic/Longyearbyen, America/Fort_Nelson, America/Caracas, America/Guadeloupe, Asia/Hebron, Indian/Kerguelen, SystemV/PST8PDT, Africa/Monrovia, Asia/Ust-Nera, Egypt, Asia/Srednekolymsk, America/North_Dakota/New_Salem, Asia/Anadyr, Australia/Melbourne, Asia/Irkutsk, America/Shiprock, America/Winnipeg, Europe/Vatican, Asia/Amman, Etc/UTC, SystemV/AST4ADT, Asia/Tokyo, America/Toronto, Asia/Singapore, Australia/Lindeman, America/Los_Angeles, SystemV/EST5EDT, Pacific/Majuro, America/Argentina/Buenos_Aires, Europe/Nicosia, Pacific/Guadalcanal, Europe/Athens, US/Pacific, Europe/Monaco] 10 11 // 获取带时区的时间 12 ZoneId zoneId1 = ZoneId.of("America/New_York"); 13 ZonedDateTime now = ZonedDateTime.now(zoneId1); // 获取指定时区(美国纽约)的时间 14 System.out.println(now); // 2024-03-19T03:12:37.262-04:00[America/New_York] 15 16 // 获取世界标准时间 17 ZonedDateTime now1 = ZonedDateTime.now(Clock.systemUTC()); 18 System.out.println(now1); // 2024-03-19T07:12:37.268Z 19 20 // 获取系统默认时区对应的时间 21 ZonedDateTime now2 = ZonedDateTime.now(); 22 System.out.println(now2); // 2024-03-19T15:12:37.268+08:00[Asia/Shanghai] 23 24 // ZonedDateTime转LocalDateTime 25 System.out.println(now2.toLocalDateTime()); // 2024-03-19T15:14:37.157 26 // ZonedDateTime转LocalDate 27 System.out.println(now2.toLocalDate()); 28 // ZonedDateTime转LocalTime 29 System.out.println(now2.toLocalTime()); 30 31 // ZonedDateTime包含了全部LocalDateTime、LocalDate、LocalTime获取、修改、增加、减少日期的操作 32 // ZonedDateTime获取年月日时分秒 33 System.out.println(now2.getYear()); 34 System.out.println(now2.getMonth()); 35 System.out.println(now2.getDayOfMonth()); 36 System.out.println(now2.getHour()); 37 System.out.println(now2.getMinute()); 38 System.out.println(now2.getSecond()); 39 }
Instant
1 public static void main(String[] args) { 2 Instant now = Instant.now(); 3 System.out.println(now); // 2024-03-19T07:28:50.257Z 4 // 时间戳, 总秒数 5 System.out.println(now.getEpochSecond()); // 1710833330 6 // 不到1秒的纳秒数 7 System.out.println(now.getNano()); // 257000000 8 9 Date date = new Date(); 10 // 获取当前毫秒值 11 System.out.println(date.getTime()); // 1710833330299 12 }
DateTimeFormatter
1 public static void main(String[] args) { 2 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); 3 4 LocalDateTime now = LocalDateTime.now(); 5 System.out.println(now); // 2024-03-19T15:36:56.256 6 7 // 方式1: 按照指定格式格式化日期 8 System.out.println(formatter.format(now)); // 2024年03月19日 15:36:56 9 // 方式2: 10 System.out.println(now.format(formatter)); // 2024年03月19日 15:36:56 11 12 // 解析指定日期格式, 根据指定的格式化器解析 13 String dateString = "2024年03月19日 15:36:56"; 14 LocalDate parseLocalDate = LocalDate.parse(dateString, formatter); 15 LocalTime parseLocalTime = LocalTime.parse(dateString, formatter); 16 LocalDateTime parseLocalDateTime = LocalDateTime.parse(dateString, formatter); 17 System.out.println(parseLocalDate); // 2024-03-19 18 System.out.println(parseLocalTime); // 15:36:56 19 System.out.println(parseLocalDateTime); // 2024-03-19T15:36:56 20 }
时间、日期间隔
1 public static void main(String[] args) { 2 3 LocalDate start = LocalDate.of(2029, 8, 10); 4 LocalDate end = LocalDate.of(2030, 8, 15); 5 6 // 计算两个日期 年月日的差值 7 Period period = Period.between(start, end); 8 System.out.println(period.getYears()); // 1 9 System.out.println(period.getMonths()); // 0 10 System.out.println(period.getDays()); // 5 11 12 13 LocalDateTime from = LocalDateTime.of(2025, 11, 11, 10, 0, 0); 14 LocalDateTime to = LocalDateTime.of(2025, 11, 12, 15, 0, 0); 15 16 // 计算两个时间对象相差的年月日时分秒等信息 (两个日期间隔多少天、多少小时、多少分钟、多少秒、多少毫秒、多少纳秒) 17 Duration duration = Duration.between(from, to); 18 System.out.println(duration.toDays()); // 1 19 System.out.println(duration.toHours()); // 29 (24+5小时) 20 System.out.println(duration.toMinutes()); // 1740 21 System.out.println(duration.getSeconds()); // 104400 22 System.out.println(duration.toMillis()); // 104400000 23 System.out.println(duration.toNanos()); // 104400000000000 24 }
4、运算语法
分组并进行求和组合运算
示例代码:
1 List<String> items =
2 Arrays.asList("apple", "apple", "banana",
3 "apple", "orange", "banana", "papaya");
4
5 Map<String, Long> result =
6 items.stream().collect(
7 Collectors.groupingBy(
8 Function.identity(), Collectors.counting()
9 )
10 );
11
12 System.out.println(result);
运行结果:
{papaya=1, orange=1, banana=2, apple=3}
或者针对对象的复杂处理:
1 /* 先按slotId进行分组,再对分好组之后的数据进行求和5 */
6 Map<Long, Long> adNumMap = adSlotDataList.stream().collect(Collectors.groupingBy(AdSlotData::getSlotId, Collectors.summingLong(AdSlotData::getAdNum)));
List转换成Map
示例代码:
1 /**
2 * List -> Map
3 * 需要注意的是:
4 * toMap 如果集合对象有重复的key,会报错Duplicate key ....
5 * apple1,apple12的id都为1。
6 * 可以用 (k1,k2)->k1 来设置,如果有重复的key,则保留key1,舍弃key2
7 */
8 Map<Long, AdSlotData> adPVNumMap = adSlotDataList.stream().collect(Collectors.toMap(AdSlotData::getSlotId, adSlotData -> adSlotData, (k1, k2) -> k1));
5、函数接口
函数式接口新特性
java8中引入了函数式接口新特性,使用@FunctionalInterface标识,表示有且只有一个抽象方法,但可以有多个非抽象方法。 1 package com.notes.java8.functionInterface;
2
3 /**
4 * 文件描述 函数式接口:
5 * 有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
6 **/
7 @FunctionalInterface
8 public interface Hello {
9
10 /**
11 * abstract 方法,只能有一个
12 */
13 void hello();
14
15 /**
16 * 允许定义默认方法
17 */
18 default void hi(){
19 System.out.println("this is default method");
20 }
21
22 /**
23 * 允许定义静态方法
24 */
25 static void hei() {
26 System.out.println("this is static method");
27 }
28
29 /**
30 * 允许定义 java.lang.Object 里的 public 方法
31 */
32 @Override
33 boolean equals(Object obj);
34 }
四大基本函数式接口
Function<T, R>
根据其中抽象方法的参数列表和返回值类型,可以得知可以在方法中对传入的参数计算和转换,把结果返回R apply(T t);
eg:
1 User user = new User(88, "bb");
2 String name = uft.apply(user);
3 System.out.println(name);
4
5
6 /**
7 * Function<T, R> lambda写法
8 */
9 private static Function<User, String> uft = u -> u.getName();
BiFunction<T, R, U>
接收两个参数,返回一个结果
Consumer<T>
根据其中抽象方法的参数列表和返回值类型,可以得知可以在方法中对传入的参数进行消费接受一个参数T,没有返回值,使用accept方法对参数执行操作
void accept(T t);
eg:
1 User user = new User(88, "bb");
2
3 uc.accept(user);
4
5 /**
6 * Consumer<T> lambda写法
7 */
8 private static Consumer<User> uc = u -> System.out.println(u.getName());
BiConsumer<T, U>
接口两个参数,没有返回值,也就是消费两个入参
Supplier<T>
根据其中抽象方法的参数列表和返回值类型,可以得知可以在方法中创建对象,把创建好的对象返回T get();
eg:
1 User user1 = us.get();
2 System.out.println(user1.getName());
3
4 /**
5 * Supplier<T> lambda写法
6 */
7 private static Supplier<User> us = () -> new User(1, "us");
Predicate<T>
根据其中抽象方法的参数列表和返回值类型,可以得知可以在方法中对传入的参数条件判断,返回判断结果boolean test(T t);
eg:
1 boolean test = up.test(user);
2 System.out.println(test);
3
4 /**
5 * Predicate<T>
6 */
7 private static Predicate<User> up = u -> !u.getName().isEmpty();
其他的函数式接口可参见以下接口示意图:
常用的默认方法
and
在使用Predicate接口的时候,可能需要进行判断条件的拼接,而and方法就相当于使用&&来拼接两个判断条件
1 public class Test { 2 3 public static void main(String[] args) { 4 List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22); 5 // Predicate接口中的and方法使用 6 List<Integer> collect = list.stream().filter( 7 new Predicate<Integer>() { 8 @Override 9 public boolean test(Integer x) { 10 return x > 10; 11 } 12 }.and(new Predicate<Integer>() { 13 @Override 14 public boolean test(Integer y) { 15 return y < 30; 16 } 17 }) 18 ).collect(Collectors.toList()); 19 System.out.println(collect); // [29, 22] 20 // lambda简化 21 List<Integer> collect2 = list.stream().filter( 22 ((Predicate<Integer>) x -> x > 10).and(y -> y < 30) 23 ).collect(Collectors.toList()); 24 } 25 }
自定义方法
1 public class Test { 2 3 public static void main(String[] args) { 4 List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22); 5 6 handle(list, new IntPredicate() { 7 @Override 8 public boolean test(int i) { 9 return i > 10; 10 } 11 }, new IntPredicate() { 12 @Override 13 public boolean test(int i) { 14 return i < 25; 15 } 16 }); 17 18 // lambda简化 19 handle(list, i -> i > 10, i -> i < 25); 20 } 21 22 23 public static void handle(List<Integer> list, IntPredicate predicate, IntPredicate predicate2) { 24 for (Integer x : list) { 25 // 打印同时满足两个条件判断的值 26 if (predicate.and(predicate2).test(x)) { // and方法返回的是一个Predicate, 需要调用test方法 27 System.out.println(x); 28 } 29 } 30 } 31 }
or
在使用Predicate接口的时候,可能需要进行判断条件的拼接,而 or 方法就相当于使用 || 来拼接两个判断条件
1 public class Test { 2 3 public static void main(String[] args) { 4 List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22); 5 // Predicate接口中的or方法使用 6 List<Integer> collect = list.stream().filter( 7 new Predicate<Integer>() { 8 @Override 9 public boolean test(Integer x) { 10 return x > 20; 11 } 12 }.or(new Predicate<Integer>() { 13 @Override 14 public boolean test(Integer y) { 15 return y > 30; 16 } 17 }) 18 ).collect(Collectors.toList()); 19 System.out.println(collect); // [30, 29, 47, 22] 20 21 // lambda简化 22 List<Integer> collect2 = list.stream().filter( 23 ((Predicate<Integer>) x -> x > 20).or(y -> y > 30) 24 ).collect(Collectors.toList()); 25 System.out.println(collect2); 26 } 27 }
negate
Predicate接口中的方法,相当于在判断前面加了一个!,表示取反的意思
1 public class Test { 2 3 public static void main(String[] args) { 4 List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22); 5 // 遍历打印元素不大于10,也就是小于10的所有元素 6 list.stream().filter(new Predicate<Integer>() { 7 @Override 8 public boolean test(Integer x) { 9 return x>10; 10 } 11 }.negate()).forEach(System.out::println); // 1 2 12 13 // lambda简化 14 list.stream().filter(((Predicate<Integer>) x -> x > 10).negate()).forEach(System.out::println); 15 } 16 }
6、stream使
1、流的介绍
Java8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性2、流的特点
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator,用户只要给出需要对其包含的元素执行什么操作,Stream 会隐式地在内部进行遍历,做出相应的数据转换,比如 “过滤掉长度大于 10 的字符串”。而原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作。
Stream 如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了。和迭代器不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作,Stream 的并行操作依赖于 Java7 中的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。
3、流的操作类型
中间操作(intermediate )
一个流可以后面跟随零个或多个 intermediate 操作。其目的是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),仅仅调用到这类方法,并没有真正开始流的遍历。
对应方法有map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered等
终止操作(terminal )
一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用尽了,无法再被操作,这是流的最后一个操作。
Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果或者一个错误。
对应方法有forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator
短路操作(short-circuiting)
对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新 Stream。
对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。
对应方法有anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
4、流的生成
流的来源可以是集合,数组,I/O channel, 产生器generator 等。基本数值型,目前有三种对应的包装类型 Stream:IntStream、LongStream、DoubleStream。
1 public class Test {
2
3 public static void main(String[] args) {
4 /*************流的来源*************/
5 // 1、of方法
6 // of(T... values):返回含有多个T元素的Stream
7 // of(T t):返回含有一个T元素的Stream
8 Stream<String> single = Stream.of("a");
9 Stream<String> multiple = Stream.of("a", "b", "c");
10
11 // 2、generator方法,返回一个无限长度的Stream,其元素由Supplier接口的提供。
12 Stream<String> generate = Stream.generate(() -> "a");
13 Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
14 @Override
15 public Double get() {
16 return java.lang.Math.random();
17 }
18 });
19 // lambda写法
20 Stream<Double> generateB = Stream.generate(() -> java.lang.Math.random());
21 Stream<Double> generateC = Stream.generate(java.lang.Math::random);
22
23 // 3、iterate方法,返回的也是一个无限长度的Stream,与generate方法不同的是,其是通过函数f迭代对给指定的元素种子而产生无限连续有序Stream,其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环
24 Stream<Integer> iterate = Stream.iterate(1, item -> item + 2);
25 // 无限流处理
26 iterate.limit(10).forEach(System.out::println);
27
28 // 4、empty方法返回一个空的顺序Stream,该Stream里面不包含元素项。
29 Stream<Object> empty = Stream.empty();
30
31 // 5、Collection接口和数组的默认方法
32 String chars[] = new String[] {"a", "b", "c"};
33 Arrays.stream(chars).forEach(System.out::println);
34
35 List list = Arrays.asList(chars);
36 list.stream().forEach(System.out::println);
37 }
38 }
① 流创建
1 public class Test { 2 3 public static void main(String[] args) throws Exception { 4 // 创建流 5 // 1、单列集合,例如: list set..., 使用 集合对象.stream() 6 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); 7 Stream<Integer> stream = list.stream(); 8 // 流的过滤操作 9 stream.filter(new Predicate<Integer>() { 10 @Override 11 public boolean test(Integer integer) { 12 return integer>3; 13 } 14 }).forEach(System.out::println); // 终结操作, 遍历流中的每个元素 15 16 // 2、数组:使用Arrays.stream()或者Stream.of() 17 Integer[] arr = {1,2,3,4,5}; 18 // 2.1 19 Stream<Integer> arrStream = Arrays.stream(arr); 20 arrStream.forEach(System.out::println); 21 // 2.2 22 Stream<Integer> integerStream = Stream.of(arr); 23 integerStream.forEach(System.out::println); 24 25 // 3、双列集合, 转换为单列集合再创建Stream 26 Map<String, Integer> hashMap = new HashMap<>(); 27 hashMap.put("a", 1); 28 hashMap.put("b", 2); 29 hashMap.put("c", 3); 30 Set<Map.Entry<String, Integer>> entrySet = hashMap.entrySet(); 31 Stream<Map.Entry<String, Integer>> entryStream = entrySet.stream(); 32 entryStream.forEach(System.out::println); 33 } 34 }
② 中间操作
1 public class Test { 2 3 public static void main(String[] args) throws Exception { 4 // 中间操作 5 6 // 1、filter, 满足指定条件的数据保留在流中 7 List<Integer> list = Arrays.asList(1, 2, 2, 3, 3, 4, 5); 8 9 // 需求:将集合中大于3的元素打印 10 11 // 1.1 匿名内部类方式 12 list.stream().filter(new Predicate<Integer>() { 13 @Override 14 public boolean test(Integer integer) { 15 return integer > 3; 16 } 17 }).forEach(System.out::println); 18 // 1.1 lambda方式 19 list.stream().filter(x -> x > 3).forEach(System.out::println); 20 21 // 需求:将集合中元素转换成字符串打印, 如果集合中是对象, 也可以将流中原本每个元素为对象, 提取成对象中的属性, 从而进行操作 22 23 // 2、map, 类型转换 24 // 2.1 匿名内部类方式 25 list.stream().map(new Function<Integer, String>() { 26 @Override 27 public String apply(Integer integer) { 28 return String.valueOf(integer); 29 } 30 }).forEach(System.out::println); 31 // 2.2 lambda方式 32 list.stream().map(String::valueOf).forEach(System.out::println); 33 34 // 3、distinct, 去重, 依赖于Object的equals方法来判断是否相同的对象, 所以如果是自定义类型, 则需要重写equals方法。 35 list.stream().distinct().forEach(System.out::println); 36 37 // 4、sorted, 排序, 如果sorted()不带参数, 并且流中为自定义的类, 那么这个类需要实现Comparable接口 38 list.stream().distinct().sorted().forEach(System.out::println); // sorted 默认升序 39 list.stream().distinct().sorted(new Comparator<Integer>() { 40 @Override 41 public int compare(Integer t1, Integer t2) { 42 return t2 - t1; // 返回值小于0, 降序 43 } 44 }).forEach(System.out::println); 45 46 // 5、limit, 设置流的最大长度, 超出的部分被抛弃 47 list.stream().distinct().sorted().limit(3).forEach(System.out::println); 48 49 // 6、skip, 跳过流中的前N个元素, 返回剩下的元素 50 list.stream().distinct().sorted().skip(2).forEach(System.out::println); 51 52 // 7、flatMap 53 // map只能将一个对象转换成另外一个对象作为流中的元素, 而flatMap可以把一个对象转换成多个对象作为流中的元素 54 List<List<Integer>> lists = new ArrayList<>(); 55 List<Integer> tempList1 = Arrays.asList(1, 2, 3, 4, 5); 56 List<Integer> tempList2 = Arrays.asList(1, 2, 3, 4, 5); 57 List<Integer> tempList3 = Arrays.asList(1, 2, 3, 4, 5); 58 lists.add(tempList1); 59 lists.add(tempList2); 60 lists.add(tempList3); 61 // 第一个泛型为流中每个元素类型, 第二个泛型为要转换的流的类型 62 // 流中的元素类型由List<Integer> 转变成 Integer 63 lists.stream().flatMap(new Function<List<Integer>, Stream<Integer>>() { 64 @Override 65 public Stream<Integer> apply(List<Integer> integers) { 66 return integers.stream(); 67 } 68 }).forEach(System.out::println); 69 }
③ 终结操作
1 public class Test { 2 3 public static void main(String[] args) throws Exception { 4 // 终结操作 5 List<Integer> list = Arrays.asList(1, 3, 3, 2, 2, 4, 5); 6 7 // 1、forEach, 对流中的元素进行遍历操作 8 list.stream().distinct().forEach(System.out::println); 9 10 // 2、count, 获取流中的元素的个数 11 long count = list.stream().distinct().count(); 12 13 // 3、max、min 获取流中的最值 14 // max->匿名内部类方式 15 Optional<Integer> max = list.stream().distinct().max(new Comparator<Integer>() { 16 // 比较用来排序的两个参数.根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数.分别返回 -1、0 或 1 17 // t1 要比较的第一个对象 18 // t2 要比较的第二个对象 19 // Compare的返回值反应的是两个参数的权重,返回值为1时,前者(o1)权重大,返回值为-1时,后者(o2)权重大. 20 // 然后排序结果按照权重(注意这里不是值的大小)由小到大进行排序.即权重默认升序排列 21 @Override 22 public int compare(Integer t1, Integer t2) { 23 return t1 - t2; 24 } 25 }); 26 System.out.println(max.get()); 27 // max->lambda方式 28 Optional<Integer> max2 = list.stream().distinct().max(Comparator.comparingInt(t -> t)); 29 System.out.println(max2.get()); 30 31 // min->匿名内部类方式 32 Optional<Integer> min = list.stream().distinct().min(new Comparator<Integer>() { 33 @Override 34 public int compare(Integer t1, Integer t2) { 35 return t1 - t2; 36 } 37 }); 38 System.out.println(min.get()); 39 // min->lambda方式 40 Optional<Integer> min2 = list.stream().distinct().min(Comparator.comparingInt(t -> t)); 41 System.out.println(min2.get()); 42 43 // 4、collect, 把流转换成一个集合 44 45 // 4.1将流操作完之后转换成list集合 46 List<Integer> listStream = list.stream().distinct().collect(Collectors.toList()); 47 // 4.2将流操作完之后转换成set集合 48 Set<Integer> setStream = list.stream().distinct().collect(Collectors.toSet()); 49 // 4.3将流操作完之后转换成map集合 50 Map<String, String> mapStream = list.stream().distinct().collect( 51 Collectors.toMap( 52 // map的key转换方式, 流中的元素为Integer类型, 此处将Integer类型的元素转换成String类型作为map的key 53 // 如果流中的元素为对象, 那么可以使用对象的某个属性作为key 54 new Function<Integer, String>() { 55 @Override 56 public String apply(Integer integer) { 57 return String.valueOf(integer); 58 } 59 }, 60 // map的value转换方式 61 new Function<Integer, String>() { 62 @Override 63 public String apply(Integer integer) { 64 return String.valueOf(integer); 65 } 66 })); 67 // 简写方式2 68 Map<String, String> mapStream2 = list.stream().distinct().collect( 69 Collectors.toMap(item -> String.valueOf(item), item -> String.valueOf(item))); 70 // 简写方式3 71 Map<String, String> mapStream3 = list.stream().distinct().collect(Collectors.toMap(String::valueOf, String::valueOf)); 72 73 74 // 5、anyMatch, 判断流中的元素是否有至少一个符合条件的元素, 结果为boolean类型 75 76 // 判断是否存在大于3的元素 77 boolean anyMatch = list.stream().distinct().anyMatch(new Predicate<Integer>() { 78 @Override 79 public boolean test(Integer integer) { 80 return integer > 3; 81 } 82 }); 83 // lambda表达式 84 boolean anyMatch2 = list.stream().distinct().anyMatch(x -> x > 3); 85 86 // 6、allMatch, 判断流中的元素是否全都符合匹配条件, 结果为boolean类型, 如果全都符合结果为true, 否则为false 87 boolean allMatch = list.stream().distinct().allMatch(new Predicate<Integer>() { 88 @Override 89 public boolean test(Integer x) { 90 return x < 5; 91 } 92 }); 93 // lambda表达式 94 boolean allMatch2 = list.stream().distinct().allMatch(x -> x < 5); 95 96 // 7、noneMatch, 判断流中的元素是否全都不符合匹配条件, 如果都不符合结果为true, 否则为false 97 boolean noneMatch = list.stream().distinct().noneMatch(new Predicate<Integer>() { 98 @Override 99 public boolean test(Integer x) { 100 return x > 6; 101 } 102 }); 103 boolean noneMatch2 = list.stream().distinct().noneMatch(x -> x > 6); 104 105 // 8、findAny, 获取流中的任意一个元素, 该方法没有办法保证获取的一定是流中的第一个元素 106 Optional<Integer> findAny = list.stream().distinct().filter(x -> x > 10).findAny(); 107 findAny.ifPresent(System.out::println); // 如果获取的元素不为空, 则打印 108 109 // 9、findFirst, 获取流中的第一个元素 110 Optional<Integer> findFirst = list.stream().distinct().findFirst(); 111 findFirst.ifPresent(System.out::println); 112 113 // 10、reduce, 归并, 对流中的数据按照指定的计算方式计算出一个结果 114 115 116 // 先修改流中的元素类型, 然后进行reduce处理 117 // 第一个参数为计算的初始值, 第二个参数为数据处理的操作 118 String reduce = list.stream().map(String::valueOf).reduce("0", new BinaryOperator<String>() { 119 // result为每一次操作的初值, 第一次的时候为"", 也就是reduce的第一个参数的值 120 // element为遍历流中的每一个元素, 拿到这个元素进行操作, 然后返回操作的结果 121 // 这里的操作就是拿到初值, 然后将遍历的每一个元素与初值进行字符串拼接, 然后又赋值给result, 作为下一次遍历的初值 122 @Override 123 public String apply(String result, String element) { 124 return result + "-" + element; // 将流中的元素进行字符串拼接操作 125 } 126 }); 127 System.out.println(reduce); 128 129 // 需求:使用reduce计算流中的最大值 130 Integer maxReduce = list.stream().distinct().reduce(Integer.MIN_VALUE, new BinaryOperator<Integer>() { 131 @Override 132 public Integer apply(Integer result, Integer item) { 133 return Integer.max(result, item); 134 } 135 }); 136 System.out.println(maxReduce); 137 // lambda简化 138 Integer maxReduce2 = list.stream().distinct().reduce(Integer.MIN_VALUE, Integer::max); 139 140 // 需求:使用reduce计算流中的最小值 141 Integer minReduce = list.stream().distinct().reduce(Integer.MAX_VALUE, new BinaryOperator<Integer>() { 142 @Override 143 public Integer apply(Integer result, Integer item) { 144 return Integer.min(result, item); 145 } 146 }); 147 System.out.println(maxReduce); 148 // lambda简化 149 Integer minReduce2 = list.stream().distinct().reduce(Integer.MAX_VALUE, Integer::min); 150 System.out.println(minReduce2); 151 152 // 需求:使用reduce计算集合的和 153 List<String> sumList = Arrays.asList("1", "44", "33", "33", "22", "10"); 154 Integer sumReduce = sumList.stream().map(Integer::parseInt).reduce(0, new BinaryOperator<Integer>() { 155 // result 初始值为reduce的第一个参数0, 每一次循环计算的结果会赋值给result 156 // item 流中的元素, 会循环拿到每一个元素 157 @Override 158 public Integer apply(Integer result, Integer item) { 159 return result + item; 160 } 161 }); 162 System.out.println(sumReduce); 163 // lambda简化 164 Integer sumReduce2 = sumList.stream().map(Integer::parseInt).reduce(0, Integer::sum); 165 System.out.println(sumReduce2); 166 // reduce单个参数的使用方式, 其实就是将流中的第一个元素作为计算结果(result)的初始值 167 Optional<Integer> sumReduce3 = sumList.stream().map(Integer::parseInt).reduce(new BinaryOperator<Integer>() { 168 @Override 169 public Integer apply(Integer result, Integer item) { 170 return Integer.sum(result, item); 171 } 172 }); 173 sumReduce3.ifPresent(System.out::println); 174 }
注意事项:
- 惰性求值(如果没有终结操作,没有中间操作是不会得到执行的)
- 流是一次性的(一旦一个流对象经过一个终结操作后,这个流就不能再被使用)
- 不会影响原数据(在流中可以多数据做很多处理,但是正常情况下是不会影响原来集合中的元素的)
5、流的相关使用
当你熟悉使用Lambda表达式时,就可以很方便的进行流相关的操作了,提高代码的编写效率和程序的可读性。
1 public class Test {
2
3 public static void main(String[] args) throws {
4 /*************流的操作*************/
5 //concat 将两个Stream连接在一起,合成一个Stream。若两个输入的Stream都时排序的,则新Stream也是排序的;若输入的Stream中任何一个是并行的,则新的Stream也是并行的;若关闭新的Stream时,原两个输入的Stream都将执行关闭处理。
6 Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5)).forEach(integer -> System.out.print(integer + " "));
7
8 //distinct 去除掉原Stream中重复的元素,生成的新Stream中没有没有重复的元素。
9 Stream.of(1, 1, 3, 4, 3).distinct().forEach(System.out::println);
10
11 //filter 对原Stream按照指定条件过滤,过滤出满足条件的元素。
12 Stream.of(1, 1, 3, 4, 3).filter(x -> x > 2).forEach(System.out::println);
13
14 //map方法将对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。
15 //为了提高处理效率,官方已封装好了,三种变形:mapToDouble,mapToInt,mapToLong,将原Stream中的数据类型,转换为double,int或者long类型。
16 Stream.of("a", "b", "c").map(item -> item.toUpperCase()) // .map(String::toUpperCase)
17 .forEach(System.out::println);
18
19 // flatMap方法与map方法类似,都是将原Stream中的每一个元素通过转换函数转换,
20 // 不同的是,该换转函数的对象是一个Stream,也不会再创建一个新的Stream,而是将原Stream的元素取代为转换的Stream。
21 List<User> users = UserUtil.getUsers(6);
22 users.stream().flatMap(s -> s.getInterests().stream()).forEach(System.out::println);
23
24 Stream.of("a", "b", "c").flatMap(s -> Stream.of(s.toUpperCase()));
25
26 //peek 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例)
27 Stream.of("a", "b", "c")
28 //优先执行
29 .peek(s -> System.out.println("peek:" + s)).forEach(System.out::println);
30
31 //skip 将过滤掉原Stream中的前N个元素,返回剩下的元素所组成的新Stream。
32 // 如果原Stream的元素个数大于N,将返回原Stream的后的元素所组成的新Stream;
33 // 如果原Stream的元素个数小于或等于N,将返回一个空Stream。
34 Stream.of("a", "b", "c").skip(2).forEach(System.out::println);
35
36 // sorted方法将对原Stream进行排序,返回一个有序列的新Stream。sorterd有两种变体sorted(),sorted(Comparator),
37 // 前者将默认使用Object.equals(Object)进行排序,而后者接受一个自定义排序规则函数(Comparator),可自定义进行排序。
38 Stream.of(5, 6, 3, 9, 1).sorted().forEach(System.out::println);
39
40 System.out.println("+++++++++++++++++++++++++++++++");
41 Stream.of(5, 6, 3, 9, 1).sorted(new Comparator<Integer>() {
42 @Override
43 public int compare(Integer o1, Integer o2) {
44 //asc
45 return o1 - o2;
46 }
47 }).forEach(System.out::println);
48
49 Stream.of(5, 6, 3, 9, 1)
50 //desc
51 .sorted(((o1, o2) -> o2 - o1)).forEach(System.out::println);
52 System.out.println("+++++++++++++++++++++++++++++++");
53
54 // count 将返回Stream中元素的个数。
55 long count = Stream.of(1, 2, 3, 4, 5).count();
56 System.out.println("count:" + count);
57
58 // forEach 用于遍历Stream中的所元素,避免了使用for循环,让代码更简洁,逻辑更清晰。
59 Stream.of("a", "b", "c").forEach(System.out::println);
60
61 // forEachOrdered 与forEach类似,都是遍历Stream中的所有元素,
62 // 不同的是,如果该Stream预先设定了顺序,会按照预先设定的顺序执行(Stream是无序的),默认为元素插入的顺序。
63 Stream.of(5, 2, 1, 4, 3).forEachOrdered(integer -> System.out.println("integer:" + integer));
64
65 // max 根据指定的Comparator,返回一个Optional,该Optional中的value值就是Stream中最大的元素。
66 Optional<Integer> max = Stream.of(5, 2, 2, 3, 4, 8).max((o1, o2) -> o2 - o1);
67 // Optional<Integer> max3 = Stream.of(1, 2, 3, 4, 5)
68 // .max(Comparator.comparingInt(x -> x));
69 Optional<Integer> max3 = Stream.of(1, 2, 3, 4, 5).max((o1, o2) -> o1 - o2);
70 int max2 = Stream.of(1, 2, 3, 4, 5).mapToInt(x -> x).max().getAsInt();
71 System.out.println("max = " + max.get() + " max2 = " + max2 + " max3 = " + max3.orElse(-1));
72
73 UserUtil.getUsers(6).stream().sorted(Comparator.comparing(User::getName).thenComparing(User::getId)).forEach(u -> System.out.println(u.getName()));
74
75 // min 根据指定的Comparator,返回一个Optional,该Optional中的value值就是Stream中最小的元素。
76 Optional<Integer> min = Stream.of(1, 2, 3, 4, 5).min((o1, o2) -> o1 - o2);
77 System.out.println("min:" + min.get());
78
79 System.out.println("*********************reduce********************");
80 // reduce
81 // 1、reduce((sum, item) -> { ... }); //返回Optional,因为可能存在为空的情况,
82 // 2、reduce(0, (sum, item) -> { ... }); /返回对应类型,不存在为空的情况
83 //无初始值,第一个参数为stream的第一个元素,第二个参数为stream的第二个元素,计算的结果赋值给下一轮计算的sum
84 Optional<Integer> optional = Stream.of(1, 2, 3, 4, 5).reduce((sum, item) -> {
85 System.out.println("sum before:" + sum);
86 System.out.println("item:" + item);
87 sum = sum + item;
88 System.out.println("sum after:" + sum);
90 return sum;
91
92 // return Integer.sum(sum, item);
93 });
94 //等效
95 Optional<Integer> optional1 = Stream.of(1, 2, 3, 4, 5).reduce((sum, item) -> Integer.sum(sum, item));
96 //等效
97 Optional<Integer> optional2 = Stream.of(1, 2, 3, 4, 5).reduce(Integer::sum);
98 System.out.println("integer = " + optional.orElse(-1));
99 System.out.println("*****************************************");
100 //给定初始值,第一个参数为初始值,第二个参数为stream的第一个元素,计算的结果赋值给下一轮计算的sum
101 Integer reduce = Stream.of(1, 2, 3, 4, 5).reduce(5, (sum, item) -> {
102 System.out.println("sum2 before:" + sum);
103 System.out.println("item:" + item);
104 sum = sum + item;
105 System.out.println("sum2 after:" + sum);
106 return sum;
107 });
108 //等效
109 Integer reduce2 = Stream.of(1, 2, 3, 4, 5).reduce(0, (sum, item) -> Integer.sum(sum, item));
110 //等效
111 Integer reduce3 = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum);
112 System.out.println("reduce = " + reduce);
113
114 System.out.println("*********************collect********************");
115 List<Integer> toList = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
116 List<Integer> toList2 = Stream.of(1, 2, 3, 4).collect(Collectors.toCollection(ArrayList::new));
117 System.out.println("toList: " + toList);
118
119 Set<Integer> toSet = Stream.of(1, 2, 3, 4).collect(Collectors.toSet());
120 Set<Integer> toSet2 = Stream.of(1, 2, 3, 4).collect(Collectors.toCollection(() -> new TreeSet()));
121 System.out.println("toSet: " + toSet);
122
123 //(value1, value2) -> value1 用前面的value覆盖后面的value,保持不变
124 List<User> userList = UserUtil.getUsers(5);
125 userList.add(new User(2, "fjw"));
126 Map<Integer,String> toMap = userList.stream().collect(Collectors.toMap(User::getId, User::getName, (value1, value2) -> value1));
127 System.out.println("(value1, value2) -> value1");
128 toMap.forEach((k, v) -> System.out.println(k + "-" + v));
129
130 // 对value值进行了限定不能为null,否则抛出空指针异常
131 // userList.add(new User(3, null));
132 //(value1, value2) -> value2 用后面的value覆盖前面的value
133 Map<Integer,String> toMap2 = userList.stream().collect(Collectors.toMap(User::getId, User::getName, (value1, value2) -> value2));
134 System.out.println("(value1, value2) -> value2");
135 toMap2.forEach((k, v) -> System.out.println(k + "-" + v));
136
137 // 解决value值为null方式
138 userList.add(new User(4, null));
139 Map<Integer,String> toMap3 = userList.stream().collect(HashMap::new, (m, u) -> m.put(u.getId(), u.getName()), HashMap::putAll);
140 toMap3.forEach((k, v) -> System.out.println(k + "-" + v));
141
142 Optional<Integer> maxBy = Stream.of(1, 2, 3, 4).collect(Collectors.maxBy(Comparator.comparingInt(o -> o)));
143 System.out.println("maxBy:" + maxBy.get());
144 // 统计流中元素的数量, 等价于 Long counting = Stream.of(1, 2, 3, 4).count();
145 Long counting = Stream.of(1, 2, 3, 4).collect(Collectors.counting());
146 System.out.println("counting:" + counting);
147
148 // 分割数据块
149 Map<Boolean,List<Integer>> partitioningBy = Stream.of(1, 2, 3, 4, 5).collect(Collectors.partitioningBy(item -> item > 3));
150 // partitioningBy : {false=[1, 2, 3], true=[4, 5]}
151 System.out.println("partitioningBy : " + partitioningBy);
152 // 根据分组条件进行分组, 然后统计每个分组的数量
153 Map<Boolean,Long> collect = Stream.of(1, 2, 3, 4).collect(Collectors.partitioningBy(item -> item > 3, Collectors.counting()));
154 System.out.println("collect: " + collect); // {false=3, true=1}
155
156 // 数据分组
157 Map<Boolean,List<Integer>> groupingBy = Stream.of(1, 2, 3, 4, 5).collect(Collectors.groupingBy(item -> item > 3));
158 // partitioningBy : {false=[1, 2, 3], true=[4, 5]}
159 System.out.println("groupingBy : " + groupingBy);
160
161 // 字符串
162 String joining = Stream.of("a", "b", "c", "d").collect(Collectors.joining(","));
163 System.out.println(joining); // a,b,c,d
164
165 String joining2 = Stream.of("a", "b", "c", "d").collect(Collectors.joining(",", "[", "]"));
166 System.out.println(joining2); // [a,b,c,d]
167 // 根据id分组
168 Map<Integer,List<User>> collect3 = userList.stream().collect(Collectors.groupingBy(User::getId));
169 // 将id映射为id集合 List<Integer> collect1 = users.stream().map(User::getId).collect(Collectors.toList());
170 List<Integer> collect1 = userList.stream().collect(Collectors.mapping(User::getId, Collectors.toList())); // [1, 2, 3]
171 // 根据id分组后将name映射成集合
172 Map<Integer, List<String>> collect2 = userList.stream().collect(Collectors.groupingBy(User::getId, Collectors.mapping(User::getName, Collectors.toList())));
173 System.out.println(collect2); // {1=[zhangsan], 2=[lisi], 3=[wangwu], 4=[zhangsan]} id -> name176 }
177 }
6、stream基本数据类型优化
在stream的方法很多都使用了泛型,所以涉及到的参数和返回值都是引用类型。1 public class Test { 2 3 public static void main(String[] args) { 4 List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22); 5 // 通过mapToInt将流中的数据在转换的时候,包装类型转为基本类型,后续的处理也都是基本数据类型,减少装箱拆箱带来的耗时 6 list.stream().mapToInt(x -> x + 10) 7 .filter(x -> x > 25) 8 .map(x -> x + 3) 9 .forEach(System.out::println); 10 } 11 }
7、并行流
1 public class Test { 2 3 public static void main(String[] args) { 4 List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22, 11, 14, 13, 55, 92, 53); 5 // 串行计算 6 Optional<Integer> reduce = list.stream().filter(x -> x > 10).reduce(Integer::sum); 7 reduce.ifPresent(System.out::println); // 366 8 9 // 并行计算 10 Optional<Integer> reduce2 = list.stream().parallel().filter(x -> x > 10).reduce(Integer::sum); 11 reduce2.ifPresent(System.out::println); // 366 12 } 13 }
base64
java 1.8中引入了Base64,不在需要引入第三方库就可以使用base64了。
在需要用到base64进行加密解密的时候就可以使用了
1 String text = "base64 in java8 lib";
2 //编码
3 String encode = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8));
4 System.out.println(encode);
5
6 //解码
7 String decode = new String(Base64.getDecoder().decode(encode), StandardCharsets.UTF_8);
8 System.out.println(decode);