技巧

map.forEach

1
2
3
4
5
6
7
8
9
10
11
12
13
// 创建一个Map
Map<String, Object> map= new HashMap<>();
infoMap.put("a", "a");
infoMap.put("b", "b");
infoMap.put("c", "c");
// 传统的Map迭代方式
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// JDK8的迭代方式
map.forEach((key, value) -> {
System.out.println(key + ":" + value);
});

字符串与列表转换

  • 列表转换成字符串,以逗号隔开

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import org.springframework.util.StringUtils;

    List<Object> objects = new ArrayList<>();
    objects.add(1);
    objects.add(2);
    objects.add(3);

    String s = StringUtils.collectionToDelimitedString(objects, ",");
    System.out.println(s);
    // 1,2,3
  • 将字符串以逗号劈开,转换成列表

    1
    2
    3
    List<String> strings = Arrays.asList("1,2,3".split(","));
    System.out.println(strings);
    // [1, 2, 3]
  • 将List roleList 中的name获取出来,转化为list

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    List<Student> stuList = new ArrayList<>();
    stuList.add(new Student("张三", 18));
    stuList.add(new Student("李四",20));

    List<String> list = stuList.stream()
    .map(Student::getName)
    .collect(Collectors.toList());

    System.out.println(list);
    // [张三, 李四]
  • 根据所属类型分组 Collectors.groupingBy

    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
    List<Student> users = Arrays.asList(
    new Student("Jack",9),
    new Student("Kreas",10),
    new Student("Marry",13),
    new Student("Timi",14));

    Map<Integer,List<Student>> map=users.stream().collect(Collectors.groupingBy(Student::getAge));
    Map<String,List<Student>> map1=users.stream().collect(Collectors.groupingBy(i->i.getAge()+"_"+"年龄"));
    Map<String,List<Student>> map2=users.stream().collect(Collectors.groupingBy(i->{
    if(i.getAge()<10){
    return "young";
    }else{
    return "old";
    }
    }));


    System.out.println(map);
    /*
    { 9=[Student(name=Jack, age=9, subject=null)],
    10=[Student(name=Kreas, age=10, subject=null)],
    13=[Student(name=Marry, age=13, subject=null)],
    14=[Student(name=Timi, age=14, subject=null)]}
    */
    System.out.println(map1);
    /*
    { 9_年龄=[Student(name=Jack, age=9, subject=null)],
    10_年龄=[Student(name=Kreas, age=10, subject=null)],
    13_年龄=[Student(name=Marry, age=13, subject=null)],
    14_年龄=[Student(name=Timi, age=14, subject=null)]}
    */
    System.out.println(map2);
    /*
    {young=[Student(name=Jack, age=9, subject=null)],
    old=[Student(name=Kreas, age=10, subject=null),
    Student(name=Marry, age=13, subject=null),
    Student(name=Timi, age=14, subject=null)]}
    */
    System.out.println(users);
    /*
    [Student(name=Jack, age=9, subject=null),
    Student(name=Kreas, age=10, subject=null),
    Student(name=Marry, age=13, subject=null),
    Student(name=Timi, age=14, subject=null)]

    */


java8 stream流

主要是用来更好的过滤或加工成想要的内容

contains包含

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@SpringBootTest
class Study {
// stream:串行
// parallelStream:并行

/**
* 中间操作符:filter:用于通过设置的条件过滤出包含的元素
* 中止操作符:collect:收集器,将流转换其他形式
*/
@Test
void filter() {
List<String> strings = Arrays.asList("abc", "abcd", "abcde", "f");
List<String> filter = strings.stream().filter(str -> str.contains("a")).collect(Collectors.toList());
System.out.println(filter);
//[abc, abcd, abcde]
}

distinct:去除重复的

1
2
3
4
5
6
7
8
9
10
/**
* distinct:去除重复的
*/
@Test
void distinct(){
List<String> strings = Arrays.asList("abc", "abcd", "abcde", "f","","f","c","abc");
List<String> distinct = strings.stream().distinct().collect(Collectors.toList());
System.out.println(distinct);
// [abc, abcd, abcde, f, , c]
}

limit 边界长度

1
2
3
4
5
6
7
8
9
10
/**
* limit:会返回一个不超过给定长度的流
*/
@Test
void limit(){
List<String> strings = Arrays.asList("abc", "abcd", "abcde", "f","","f","c","abc");
List<String> limit = strings.stream().limit(4).collect(Collectors.toList());
System.out.println(limit);
// [abc, abcd, abcde, f]
}

anyMatch 一个满足

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 中止操作符

/**
* anyMatch: 集合中是否有一个元素满足条件
*/
@Test
public void anyMatch(){
List<String> strings = Arrays.asList("abc", "abcd", "abcde", "f","","f","c","abc");
boolean bc = strings.stream().anyMatch(s -> s.contains("bc"));
System.out.println(bc);// true

boolean q = strings.stream().anyMatch(s -> s.contains("q"));
System.out.println(q); //false
}

集体满足

1
2
3
4
5
6
7
8
9
/**
* anyMatch: 集合中元素是否满足条件
*/
@Test
public void allMatch(){
List<String> strings = Arrays.asList("abc", "abcd", "abcde", "f","","f","c","abc");
boolean length = strings.stream().allMatch(s -> s.length() > 3);
System.out.println(length);// false
}

返回元素

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* findAny:返回当前流中任意元素
* findFirst:返回当前流中第一个元素
*/
@Test
public void findAny(){
List<String> strings = Arrays.asList("abc", "abcd", "abcde", "f","","f","c","abc");
Optional<String> any = strings.stream().findAny();
System.out.println(any.get()); //abc

Optional<String> first = strings.stream().findFirst();
System.out.println(first.get()); //abc
}

forEach 简单的循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
void forEach(){
List<String> strings = Arrays.asList("abc", "abcd", "abcde", "f","","f","c","abc");
strings.stream().forEach(s -> System.out.println(s));
/*
abc
abcd
abcde
f

f
c
abc
*/
}

skip 去掉前面 n 个元素

1
2
3
4
5
6
7
8
9
10
11

/**
* skip:去掉前面 n 个元素
*/
@Test
void skip(){
List<String> strings = Arrays.asList("abc","bc","bc","efg","abcd","","f","jkl");
List<String> collect = strings.stream().skip(3).collect(Collectors.toList());
System.out.println(collect);
// [efg, abcd, , f, jkl]
}

map : 对流中的所有元素作统一处理

1
2
3
4
5
6
7
8
9
10
/**
* map : 对流中的所有元素作统一处理
*/
@Test
void map(){
List<String> strings = Arrays.asList("abc","bc","bc","efg","abcd","","f","jkl");
List<String> collect = strings.stream().map(str -> "Hulu_" + str.concat("_Bye")).collect(Collectors.toList());
System.out.println(collect);
// [Hulu_abc_Bye, Hulu_bc_Bye, Hulu_bc_Bye, Hulu_efg_Bye, Hulu_abcd_Bye, Hulu__Bye, Hulu_f_Bye, Hulu_jkl_Bye]
}

flatMap 小集合合并成大集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* flatmap的核心作用:把几个小的集合分别转换成流,再把各个流转换为一个流,流转集合,以此达到小集合合并到大集合的作用。
*/
@Test
void en(){
Student z3 = new Student("z3", 22,Arrays.asList("语文", "数学"));
Student l4 = new Student("l4", 18,Arrays.asList("英语", "化学"));
Student w5 = new Student("w5", 20,Arrays.asList("科学"));

List<Student> students = Arrays.asList(z3, l4, w5);
List<String> collect = students.stream().flatMap(stu -> stu.getSubject().stream()).collect(Collectors.toList());

System.out.println(collect);
}
//[语文, 数学, 英语, 化学, 科学]

sorted:排序

Comparator.comparing () 对象特定排序
更多排序参考:
https://www.cnblogs.com/zhangliang88/p/14341348.html

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
/**
* sorted:排序,默认按照code排序,也可以传参排序
*/
@Test
void sorted(){
List<String> strings = Arrays.asList("abc","bc","bc","efg","abcd","","f","jkl");
List<String> collect = strings.stream().sorted().collect(Collectors.toList());
System.out.println(collect);
// [, abc, abcd, bc, bc, efg, f, jkl]
Collator.getInstance(Locale.CHINA);
}


/**
Comparator.comparing () 对象特定排序
例子:根据学生年龄排序
*/
@Test
void paiXu(){
Student z3 = new Student("z3", 22);
Student l4 = new Student("l4", 18);
Student w5 = new Student("w5", 20);
ArrayList<Student> objects = new ArrayList<>();
objects.add(z3);
objects.add(l4);
objects.add(w5);

List<Student> collect = objects.stream().sorted(Comparator.comparing(Student::getAge)).collect(Collectors.toList());
System.out.println(collect);
}

// [Student(name=l4, age=18, subject=null), Student(name=w5, age=20, subject=null), Student(name=z3, age=22, subject=null)]

summaryStatistics 统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    /**
* mapToInt:将list里面的 Integer 转话为 int (大概是这么个意思),并且转换为int流
* summaryStatistics:将int流分别计算 平均数、总数、最大、最小值
*/
@Test
void summaryStatistics(){
List<Integer> primes = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
IntSummaryStatistics stats = primes.stream().mapToInt(x->x).summaryStatistics();
System.out.println("max : " + stats.getMax());
System.out.println("min : " + stats.getMin());
System.out.println("sum : " + stats.getSum());
System.out.println("average : " + stats.getAverage());

// max : 10
// min : 1
// sum : 55
// average : 5.5
}
}

java8 日期时间类

部分转载自
https://blog.csdn.net/lemon_TT/article/details/109145432

LocalDate

只会获取年月日

1
2
3
4
//获取当前年月日  
LocalDate localDate = LocalDate.now();
//构造指定的年月日
LocalDate localDate1 = LocalDate.of(2020, 10, 10);

获取年、月、日、星期几

1
2
3
4
5
6
7
8
9
10
11
12
//获取年
int year = localDate.getYear();
int year1 = localDate.get(ChronoField.YEAR);
//获取月份
Month month = localDate.getMonth();
int month1 = localDate.get(ChronoField.MONTH_OF_YEAR);
//获取天
int day = localDate.getDayOfMonth();
int day1 = localDate.get(ChronoField.DAY_OF_MONTH);
//获取星期几
DayOfWeek dayOfWeek = localDate.getDayOfWeek();
int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK);

LocalTime

只会获取几点几分几秒

1
2
LocalTime localTime = LocalTime.of(13, 51, 10);  
LocalTime localTime1 = LocalTime.now();
1
2
3
4
5
6
7
8
9
//获取小时  
int hour = localTime.getHour();
int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);
//获取分
int minute = localTime.getMinute();
int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);
//获取秒
int second = localTime.getSecond();
int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);

LocalDateTime

获取年月日时分秒,等于LocalDate+LocalTime

1
2
3
4
5
LocalDateTime localDateTime = LocalDateTime.now();  
LocalDateTime localDateTime1 = LocalDateTime.of(2020, Month.SEPTEMBER, 10, 14, 46, 56);
LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime);
LocalDateTime localDateTime3 = localDate.atTime(localTime);
LocalDateTime localDateTime4 = localTime.atDate(localDate);
1
2
3
4
// 获取LocalDate
LocalDate localDate2 = localDateTime.toLocalDate();
// 获取LocalTime
LocalTime localTime2 = localDateTime.toLocalTime();

Instant

获取秒数
如果只是为了获取秒数或者毫秒数,使用System.currentTimeMillis()来得更为方便

1
2
3
4
5
Instant instant = Instant.now();
// 获取秒数
long currentSecond = instant.getEpochSecond();
// 获取毫秒数
long currentMilli = instant.toEpochMilli();

日期时间的修改与计算

LocalDate、LocalTime、LocalDateTime、Instant为不可变对象,修改这些对象对象会返回一个副本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LocalDateTime localDateTime = LocalDateTime.of(2020, Month.SEPTEMBER, 10,  
14, 46, 56);
//增加一年
localDateTime = localDateTime.plusYears(1);
localDateTime = localDateTime.plus(1, ChronoUnit.YEARS);
//减少一个月
localDateTime = localDateTime.minusMonths(1);
localDateTime = localDateTime.minus(1, ChronoUnit.MONTHS);


//使用with进行修改
//修改年为2020
localDateTime = localDateTime.withYear(2020);
//修改为2022
localDateTime = localDateTime.with(ChronoField.YEAR, 2022);

另外比如有些时候想知道这个月的最后一天是几号、下个周末是几号,通过提供的时间和日期API可以很快得到答案,比如通过firstDayOfYear()返回了当前日期的第一天日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
System.out.println("//本月的第一天");
System.out.println(localDate.now().with(TemporalAdjusters.firstDayOfMonth()));

System.out.println("//今年的程序员日");
System.out.println(LocalDate.now().with(TemporalAdjusters.firstDayOfYear()).plusDays(255));

System.out.println("//今天之前的一个周六");
System.out.println(LocalDate.now().with(TemporalAdjusters.previous(DayOfWeek.SATURDAY)));

System.out.println("//本月最后一个工作日");
System.out.println(LocalDate.now().with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)));

System.out.println("//自定义逻辑");
System.out.println(LocalDate.now().with(temporal -> temporal.plus(ThreadLocalRandom.current().nextInt(100), ChronoUnit.DAYS)));

Java 8 中有一个专门的类 Period 定义了日期间隔,通过 Period.between 得到了两个 LocalDate的差,返回的是两个日期差几年零几月零几天。如果希望得知两个日期之间差几天,直接调用PeriodgetDays() 方法得到的只是最后的“零几天”,而不是算总的间隔天

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) throws Exception {
System.out.println("//计算日期差");
LocalDate today = LocalDate.of(2019, 12, 12);
LocalDate specifyDate = LocalDate.of(2019, 10, 1);
System.out.println(Period.between(specifyDate, today).getDays());
System.out.println(Period.between(specifyDate, today));
System.out.println(ChronoUnit.DAYS.between(specifyDate, today));
}
// 计算日期差
//11
//P2M11D
//72

格式化日期(常用)

DateTimeFormatter默认提供了多种格式化方式,如果默认提供的不能满足要求,可以通过DateTimeFormatter的ofPattern方法创建自定义格式化方式

1
2
3
4
5
6
7
8
9
10
11
LocalDate localDate1 = LocalDate.parse("20201010", DateTimeFormatter.BASIC_ISO_DATE);
System.out.println("localDate1 = " + localDate1);
LocalDate localDate2 = LocalDate.parse("2020-10-10", DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println("localDate2 = " + localDate2);
// localDate1 = 2020-10-10
// localDate2 = 2020-10-10

// 自定义
LocalDateTime longTime = LocalDateTime.of(1999, 10, 2, 1, 2,3);
System.out.println(longTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// 1999-10-02 01:02:03

自定义DateTimeFormatterBuilder。用得比较少

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 自定义模板
DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR)
.appendLiteral("/")
.appendValue(ChronoField.MONTH_OF_YEAR)
.appendLiteral("/")
.appendValue(ChronoField.DAY_OF_MONTH)
.appendLiteral(" ")
.appendValue(ChronoField.HOUR_OF_DAY)
.appendLiteral(":")
.appendValue(ChronoField.MINUTE_OF_HOUR)
.appendLiteral(":")
.appendValue(ChronoField.SECOND_OF_MINUTE)
.appendLiteral(".")
.appendValue(ChronoField.MILLI_OF_SECOND)
.toFormatter();

//使用模板
System.out.println(LocalDateTime.now().format(dateTimeFormatter));
// 2023/1/19 9:50:22.624

时区

Java 8 推出了新的时间日期类 ZoneId、ZoneOffset、LocalDateTime、ZonedDateTime和 DateTimeFormatter,处理时区问题更简单清晰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
String stringDate = "2020-01-02 22:00:00";
ZoneId timeZoneSH = ZoneId.of("Asia/Shanghai");
ZoneId timeZoneNY = ZoneId.of("America/New_York");
ZoneId timeZoneJST = ZoneOffset.ofHours(9);

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
ZonedDateTime date = ZonedDateTime.of(LocalDateTime.parse(stringDate, dateTimeFormatter), timeZoneJST);

DateTimeFormatter outputFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z");
System.out.println(timeZoneSH.getId() +"------"+ outputFormat.withZone(timeZoneSH).format(date));
System.out.println(timeZoneNY.getId() +"------"+ outputFormat.withZone(timeZoneNY).format(date));
System.out.println(timeZoneJST.getId() +"------"+ outputFormat.withZone(timeZoneJST).format(date));

// 输出
Asia/Shanghai------2020-01-02 21:00:00 +0800
America/New_York------2020-01-02 08:00:00 -0500
+09:00------2020-01-02 22:00:00 +0900

前后端日期时间转化问题

项目一般在实体类上加@DatetimeFormat@JsonFormat注解
两个需要同时加,否则会有时区的问题

1
2
3
4
@ApiModelProperty("创建时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;

或者在配置文件加

1
2
3
#时间戳统一转换
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

消失的八小时

  • 日期字符串存入DB后差8小时
    在后端与数据库交互的时候,可能会遇到一个问题,就是往DB中存储了一个时间字段之后,后面再查询的时候,就会发现时间数值差了8个小时,这个需要在DB的连接信息中指定下时区信息:
    spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai

  • 界面时间与后台时间差8小时
    在有一些前后端交互的项目中,可能会遇到一个问题,就是前端选择并保存了一个时间信息,再查询的时候就会发现与设置的时间差了8个小时,这个其实就是后端时区转换设置的问题。SpringBoot的配置文件中,需要指定时间字符串转换的时区信息:
    spring.jackson.time-zone=GMT+8
    这样从接口json中传递过来的时间信息,jackson框架可以根据对应时区转换为正确的Date数据进行处理。

模板解释

字母 使用说明
yyyy 4位数的年
yy 显示2位数的年份,比如2022年,则显示为22年
MM 显示2位数的月份,不满2位数的,前面补0,比如7月份显示07月
M 月份,不满2位的月份不会补0
dd 天, 如果1位数的天数,则补0
d 天,不满2位数字的,不补0
HH 24小时制的时间显示,小时数,两位数,不满2位数字的前面补0
H 24小时制的时间显示,小时数,不满2位数字的不补0
hh 12小时制的时间显示,小时数,两位数,不满2位数字的前面补0
ss 秒数,不满2位的前面补0
s 秒数,不满2位的不补0
SSS 毫秒数
z 时区名称,比如北京时间东八区,则显示CST
Z 时区偏移信息,比如北京时间东八区,则显示+0800