JAVA8新特性集

4/18/2022 JAVA8

# JAVA8新特性集🧨

# Stream流🎇

# filter使用

// Stream流的filter使用
// 方法用于通过设置的条件过滤出元素。
// 示例二:通过与 findAny 得到 if/else 的值

List<String> list = Arrays.asList("张三", "李四", "王五");
String result3 = list.stream().filter(str -> "李四".equals(str)).findAny().orElse("找不到!");
String result4 = list.stream().filter(str -> "李二".equals(str)).findAny().orElse("找不到!");

System.out.println("stream 过滤之后 2:" + result3);
System.out.println("stream 过滤之后 3:" + result4);
//stream 过滤之后 2:李四
//stream 过滤之后 3:找不到!

// 示例三:通过与 mapToInt 计算和
List<User> lists = new ArrayList<User>();
lists.add(new User(6, "张三"));
lists.add(new User(2, "李四"));
lists.add(new User(3, "王五"));
lists.add(new User(1, "张三"));
// 计算这个list中出现 "张三" id的值
int sum = lists.stream().filter(u -> "张三".equals(u.getName())).mapToInt(u -> u.getId()).sum();
System.out.println("计算结果:" + sum); 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# flatMap使用

// Stream流的flatMap使用
// flatMap 方法用于映射每个元素到对应的结果,一对多


String worlds = "The way of the future";
List<String> list7 = new ArrayList<>();
list7.add(worlds);
List<String> list8 = list7.stream().flatMap(str -> Stream.of(str.split(" ")))
   .filter(world -> world.length() > 0).collect(Collectors.toList());
System.out.println("单词:");
list8.forEach(System.out::println);
 // 单词:
 // The 
 // way 
 // of 
 // the 
 // future
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# limit使用

//示例一:获取前n条数的数据
Random rd = new Random();
System.out.println("取到的前三条数据:");
rd.ints().limit(3).forEach(System.out::println);
// 取到的前三条数据:
// 1167267754
// -1164558977
// 1977868798

// 示例二:结合skip使用得到需要的数据:skip表示的是扔掉前n个元素
List<User> list9 = new ArrayList<User>();
 for (int i = 1; i < 4; i++) {
  User user = new User(i, "pancm" + i);
  list9.add(user);
 }
System.out.println("截取之前的数据:");
// 取前3条数据,但是扔掉了前面的2条,可以理解为拿到的数据为 2<=i<3 (i 是数值下标)
List<String> list10 = list9.stream().map(User::getName).limit(3).skip(2).collect(Collectors.toList());
 System.out.println("截取之后的数据:" + list10);
//  截取之前的数据:
//  姓名:pancm1
//  姓名:pancm2
//  姓名:pancm3
//  截取之后的数据:[pancm3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# sort使用

sorted方法用于对流进行升序排序

// 示例一:随机取值排序
Random rd2 = new Random();
System.out.println("取到的前三条数据然后进行排序:");
rd2.ints().limit(3).sorted().forEach(System.out::println);
// 取到的前三条数据然后进行排序:
// -2043456377
// -1778595703
// 1013369565

// 示例二:优化排序  tips:先获取在排序效率会更高!

//普通的排序取值
List<User> list11 = list9.stream().sorted((u1, u2) -> u1.getName().compareTo(u2.getName())).limit(3)
   .collect(Collectors.toList());
System.out.println("排序之后的数据:" + list11);

//优化排序取值
List<User> list12 = list9.stream().limit(3).sorted((u1, u2) -> u1.getName().compareTo(u2.getName()))
   .collect(Collectors.toList());
System.out.println("优化排序之后的数据:" + list12);
//排序之后的数据:[{"id":1,"name":"pancm1"}, {"id":2,"name":"pancm2"}, {"id":3,"name":"pancm3"}]
//优化排序之后的数据:[{"id":1,"name":"pancm1"}, {"id":2,"name":"pancm2"}, {"id":3,"name":"pancm3"}]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# peek使用

peek对每个元素执行操作并返回一个新的Stream

// 示例:双重操作
System.out.println("peek使用:");
 Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3).peek(e -> System.out.println("转换之前: " + e)).map(String::toUpperCase).peek(e -> System.out.println("转换之后: " + e)).collect(Collectors.toList());
 
 // 转换之前: three
 // 转换之后: THREE
 // 转换之前: four
 // 转换之后: FOUR
1
2
3
4
5
6
7
8

# max/min/distinct使用

// 示例一:得到最大最小值
List<String> list13 = Arrays.asList("zhangsan","lisi","wangwu","xiwqjing");

int maxLines = list13.stream().mapToInt(String::length).max().getAsInt();
int minLines = list13.stream().mapToInt(String::length).min().getAsInt();
System.out.println("最长字符的长度:" + maxLines+",最短字符的长度:"+minLines);
//最长字符的长度:8,最短字符的长度:4

//示例二:得到去重之后的数据
String lines = "good good study day day up";
List<String> list14 = new ArrayList<String>();
list14.add(lines);
List<String> words = list14.stream().flatMap(line -> Stream.of(line.split(" "))).filter(word -> word.length() > 0).map(String::toLowerCase).distinct().sorted().collect(Collectors.toList());
System.out.println("去重复之后:" + words);
//去重复之后:[day, good, study, up]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Match使用

  • allMatch:Stream 中全部元素符合则返回 true ;

  • anyMatch:Stream 中只要有一个元素符合则返回 true;

boolean all = lists.stream().allMatch(u -> u.getId() > 3);
System.out.println("是否都大于3:" + all);

boolean any = lists.stream().anyMatch(u -> u.getId() > 3);
System.out.println("是否有一个大于3:" + any);

boolean none = lists.stream().noneMatch(u -> u.getId() > 3);
System.out.println("是否没有一个大于3的:" + none);  

 // 是否都大于3:false
 // 是否有一个大于3:true
 // 是否没有一个大于3的:false
1
2
3
4
5
6
7
8
9
10
11
12

# reduce使用

reduce 主要作用是把 Stream 元素组合起来进行操作

// 示例一:字符串连接
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println("字符串拼接:" + concat);

// 示例二:得到最小值
double minValue = Stream.of(-4.0, 1.0, 3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println("最小值:" + minValue);
//最小值:-4.0

// 示例三:求和
// 求和, 无起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println("有无起始值求和:" + sumValue);

// 求和, 有起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(1, Integer::sum);
System.out.println("有起始值求和:" + sumValue);
// 有无起始值求和:10
// 有起始值求和:11

//示例四:过滤拼接
concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
System.out.println("过滤和字符串连接:" + concat);
//过滤和字符串连接:ace
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# groupingBy/partitioningBy使用

  • groupingBy:分组排序

  • partitioningBy:分区排序

// 示例一:分组排序
System.out.println("通过id进行分组排序:");
 Map<Integer, List<User>> personGroups = Stream.generate(new UserSupplier2()).limit(5)
   .collect(Collectors.groupingBy(User::getId));
 Iterator it = personGroups.entrySet().iterator();
 while (it.hasNext()) {
  Map.Entry<Integer, List<User>> persons = (Map.Entry) it.next();
  System.out.println("id " + persons.getKey() + " = " + persons.getValue());
 }
 
 // 通过id进行分组排序:
 // id 10 = [{"id":10,"name":"pancm1"}] 
 // id 11 = [{"id":11,"name":"pancm3"}, {"id":11,"name":"pancm6"}, {"id":11,"name":"pancm4"}, {"id":11,"name":"pancm7"}]



 class UserSupplier2 implements Supplier<User> {
  private int index = 10;
  private Random random = new Random();
 
  @Override
  public User get() {
   return new User(index % 2 == 0 ? index++ : index, "pancm" + random.nextInt(10));
  }
   return new User(index % 2 == 0 ? index++ : index, "pancm" + random.nextInt(10));
 }


// 示例二:分区排序
System.out.println("通过年龄进行分区排序:");
 Map<Boolean, List<User>> children = Stream.generate(new UserSupplier3()).limit(5)
   .collect(Collectors.partitioningBy(p -> p.getId() < 18));

 System.out.println("小孩: " + children.get(true));
 System.out.println("成年人: " + children.get(false));
 
 // 通过年龄进行分区排序:
 // 小孩: [{"id":16,"name":"pancm7"}, {"id":17,"name":"pancm2"}]
 // 成年人: [{"id":18,"name":"pancm4"}, {"id":19,"name":"pancm9"}, {"id":20,"name":"pancm6"}]

  class UserSupplier3 implements Supplier<User> {
  private int index = 16;
  private Random random = new Random();
 
  @Override
  public User get() {
   return new User(index++, "pancm" + random.nextInt(10));
  }
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# List根据某个属性去重

方法一:利用stream构建TreeSet进行去重

cgEndList = cgEndList.stream()
    .collect(Collectors.collectingAndThen(
                    Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(CgModel::getCgId))
                    ), ArrayList::new));
1
2
3
4

方法二:Predicate断言接口方式进行去重

List<CgDto> result = new ArrayList<>();
            endData.stream()
                    .filter(distinctByKey(CgDto::getCgId))
                    .forEach(result::add);

static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Map<Object,Boolean> seen = new ConcurrentHashMap<>();
    //putIfAbsent方法添加键值对,如果map集合中没有该key对应的值,则直接添加,并返回null,如果已经存在对应的值,则依旧为原来的值。
    //如果返回null表示添加数据成功(不重复),不重复(null==null :TRUE)
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
1
2
3
4
5
6
7
8
9
10
11

# 获取ID最大的对象

DyCgExpertDto maxExpertId = dyCgExpertDtos.stream()
        .max(Comparator.comparing(DyCgExpertDto::getId)).get();
1
2

# 循环两个list进行条件匹配给其中一个list对象的某个属性赋值

 List<CgModel> cgs = cgList.stream()
                .map(cg -> nodeInfos.stream()
                            .filter(node -> cg.getDqjdId().equals(node.getNodeId()))
                            .findFirst()
                            .map(node -> {
                                cg.setNodeName(node.getNodeName());
                                return cg;
                            }).orElse(cg))
                .collect(Collectors.toList());
1
2
3
4
5
6
7
8
9

# 合计BigDecimal金额

BigDecimal totalMoney = fkdModelList.stream()
                    .map(FkdModel::getCountMoney)
                    .reduce(BigDecimal.ZERO,BigDecimal::add);
1
2
3

# 交集差集并集

public class Test {
 
    public static void main(String[] args) {
        List<String> list1 = new ArrayList<String>();
        list1.add("1");
		list1.add("2");
		list1.add("3");
		list1.add("5");
		list1.add("6");
 
        List<String> list2 = new ArrayList<String>();
        list2.add("2");
		list2.add("3");
		list2.add("7");
		list2.add("8");
 
        // 交集
        List<String> intersection = list1.stream().filter(item -> list2.contains(item)).collect(toList());
        System.out.println("---交集 intersection---");
        intersection.parallelStream().forEach(System.out :: println);
 
        // 差集 (list1 - list2)
        List<String> reduce1 = list1.stream().filter(item -> !list2.contains(item)).collect(toList());
        System.out.println("---差集 reduce1 (list1 - list2)---");
        reduce1.parallelStream().forEach(System.out :: println);
 
        // 差集 (list2 - list1)
        List<String> reduce2 = list2.stream().filter(item -> !list1.contains(item)).collect(toList());
        System.out.println("---差集 reduce2 (list2 - list1)---");
        reduce2.parallelStream().forEach(System.out :: println);
 
        // 并集
        List<String> listAll = list1.parallelStream().collect(toList());
        List<String> listAll2 = list2.parallelStream().collect(toList());
        listAll.addAll(listAll2);
        System.out.println("---并集 listAll---");
        listAll.parallelStream().forEachOrdered(System.out :: println);
 
        // 去重并集
        List<String> listAllDistinct = listAll.stream().distinct().collect(toList());
        System.out.println("---得到去重并集 listAllDistinct---");
        listAllDistinct.parallelStream().forEachOrdered(System.out :: println);
 
        System.out.println("---原来的List1---");
        list1.parallelStream().forEachOrdered(System.out :: println);
        System.out.println("---原来的List2---");
        list2.parallelStream().forEachOrdered(System.out :: println);
 
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# LocalDateTime🤔

全新的日期时间API。在JDK1.8之前,Java处理日期、日历和时间的方式一直为社区所诟病,将 java.util.Date设定为可变类型,以及SimpleDateFormat的非线程安全使其应用非常受限。因此推出了java.time包,该包下的所有类都是不可变类型而且线程安全。

# 关键类

  • Instant:瞬时时间。

  • LocalDate:本地日期,不包含具体时间, 格式 yyyy-MM-dd。

  • LocalTime:本地时间,不包含日期. 格式 yyyy-MM-dd HH:mm:ss.SSS 。

  • LocalDateTime:组合了日期和时间,但不包含时差和时区信息。

  • ZonedDateTime:最完整的日期时间,包含时区和相对UTC或格林威治的时差。

# 获取当前的年月日时分秒

//获取当前的时间,包括毫秒 
LocalDateTime ldt = LocalDateTime.now();
System.out.println("当前年:"+ldt.getYear());  
System.out.println("当前年份天数:"+ldt.getDayOfYear());
System.out.println("当前月:"+ldt.getMonthValue());
System.out.println("当前时:"+ldt.getHour());
System.out.println("当前分:"+ldt.getMinute());
System.out.println("当前时间:"+ldt.toString());
// 输出
// 当前年:2022
// 当前年份天数:61
// 当前月:3
// 当前时:15
// 当前分:25
// 当前时间:2022-03-02T15:25:32.818
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 格式化时间

LocalDateTime ldt = LocalDateTime.now();
System.out.println("格式化时间: "+ ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
// 格式化时间: 2022-03-02 15:25:32.818
1
2
3

# 时间增减

LocalDateTime ldt = LocalDateTime.now();
System.out.println("后5天时间:"+ldt.plusDays(5));
System.out.println("前5天时间并格式化:"+ldt.minusDays(5).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); 
System.out.println("前一个月的时间:"+ldt.minusMonths(1).format(DateTimeFormatter.ofPattern("yyyyMM"))); 
System.out.println("后一个月的时间:"+ldt.plusMonths(1)); 
System.out.println("指定2099年的当前时间:"+ldt.withYear(2099)); 
//输出
// 后5天时间:2022-03-07T15:25:32.818
// 前5天时间并格式化:2022-02-25
// 前一个月的时间:202202
// 后一个月的时间:2022-04-02T15:25:32.818
// 指定2099年的当前时间:2099-03-02T15:25:32.818
1
2
3
4
5
6
7
8
9
10
11
12

# 创建指定时间

LocalDate ld3=LocalDate.of(2019, Month.NOVEMBER, 17);
LocalDate ld4=LocalDate.of(2022, 02, 11);
System.out.println(ld3);
System.out.println(ld4);
// 输出
// 2019-11-17
// 2022-02-11
1
2
3
4
5
6
7

# 时间相差比较

// 示例一: 具体相差的年月日
LocalDate ld=LocalDate.parse("2021-11-17");
LocalDate ld2=LocalDate.parse("2022-01-05");
Period p=Period.between(ld, ld2);
System.out.println("相差年: "+p.getYears()+" 相差月 :"+p.getMonths() +" 相差天:"+p.getDays());
// 相差年: 0 相差月 :1 相差天:19
// 注:这里的月份是不满足一年,天数是不满足一个月的。这里实际相差的是1月19天,也就是49天。

// 示例二:相差总数的时间
LocalDate startDate = LocalDate.of(2021, 11, 17);
LocalDate endDate = LocalDate.of(2022, 01, 05);
System.out.println("相差月份:"+ChronoUnit.MONTHS.between(startDate, endDate));
System.out.println("两月之间的相差的天数   : " + ChronoUnit.DAYS.between(startDate, endDate));
// 相差月份:1
// 两月之间的相差的天数   : 49
// 注:ChronoUnit也可以计算相差时分秒。 


// 示例三:精度时间相差
// Duration 这个类以秒和纳秒为单位建模时间的数量或数量。
Instant inst1 = Instant.now();
System.out.println("当前时间戳 : " + inst1);
Instant inst2 = inst1.plus(Duration.ofSeconds(10));
System.out.println("增加之后的时间 : " + inst2);
System.out.println("相差毫秒 : " + Duration.between(inst1, inst2).toMillis());
System.out.println("相毫秒 : " + Duration.between(inst1, inst2).getSeconds());
// 输出
// 当前时间戳 : 2022-03-02T07:30:35.261Z
// 增加之后的时间 : 2022-03-02T07:30:45.261Z
// 相差毫秒 : 10000
// 相毫秒 : 10


// 示例四:时间大小比较
LocalDateTime ldt4 = LocalDateTime.now();
LocalDateTime ldt5 = ldt4.plusMinutes(10);
System.out.println("当前时间是否大于:"+ldt4.isAfter(ldt5));
System.out.println("当前时间是否小于"+ldt4.isBefore(ldt5));
// 当前时间是否大于:false
// 当前时间是否小于true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 时区时间计算

// 示例一:通过Clock时钟类获取计算
// Clock时钟类用于获取当时的时间戳,或当前时区下的日期时间信息。

Clock clock = Clock.systemUTC();
System.out.println("当前时间戳 : " + clock.millis()); 
Clock clock2 = Clock.system(ZoneId.of("Asia/Shanghai"));
System.out.println("亚洲上海此时的时间戳:"+clock2.millis());
Clock clock3 = Clock.system(ZoneId.of("America/New_York"));
System.out.println("美国纽约此时的时间戳:"+clock3.millis());
// 当前时间戳 : 1646206235265
// 亚洲上海此时的时间戳:1646206235265
// 美国纽约此时的时间戳:1646206235266

// 示例二:通过ZonedDateTime类和ZoneId
ZoneId zoneId= ZoneId.of("America/New_York");
ZonedDateTime dateTime=ZonedDateTime.now(zoneId);
System.out.println("美国纽约此时的时间 : " + dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
System.out.println("美国纽约此时的时间 和时区: " + dateTime);
// 输出
// 美国纽约此时的时间 : 2022-03-02 02:30:35.266
// 美国纽约此时的时间 和时区: 2022-03-02T02:30:35.266-05:00[America/New_York]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21