一. Stream API
1.1 基礎
代碼參數準備:
java複制代碼package com.weige.javaskillpoint.controller;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
public class JAVA8 {
@Data
static class User {
// 姓名
private String name;
// 手機号
private String phone;
// 年齡
private Integer age;
public User(String name, String phone, Integer age) {
this.name = name;
this.phone = phone;
this.age = age;
}
}
private static List<User> getUserList() {
return new ArrayList<User>() {{
add(new User("周潤發", "166", 52));
add(new User("周星馳", "155", 42));
add(new User("劉德華", "177", 62));
add(new User("伍佰", "188", 45));
add(new User("周傳雄", "133", 40));
add(new User("甄子丹", "199", 45));
}};
}
}
場景一:知道一個List對象,如何擷取List的所有使用者id?
scss複制代碼 public static void main(String[] args) {
// 知道一個List<User>對象,如何擷取List<User>的所有使用者手機号?
List<User> userList = getUserList();
List<String> phoneList = userList.stream().map(User::getPhone).collect(Collectors.toList());
// 列印内容 - 所有使用者手機号為 [166, 155, 177, 188, 133, 199]
log.info("所有使用者手機号為 " + phoneList);
}
場景二:知道一個List對象,如何擷取List中年齡大于50的使用者?
scss複制代碼 public static void main(String[] args) {
// 知道一個List<User>對象,如何擷取List<User>中年齡大于50的使用者?
List<User> userList = getUserList();
List<User> filterUserList = userList.stream().filter(u -> u.getAge() > 50).collect(Collectors.toList());
// 列印内容 - 年齡大于50的使用者為 [JAVA8.User(name=周潤發, phone=166, age=52), JAVA8.User(name=劉德華, phone=177, age=62)]
log.info("年齡大于50的使用者為 " + filterUserList);
}
場景三:知道一個List對象,如何按照年齡從小到小排序,從大到小排序?
scss複制代碼public static void main(String[] args) {
// 知道一個List<User>對象,如何按照年齡從大到小排序?
List<User> userList = getUserList();
List<User> ascUserList = userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
List<User> reversedUserList = userList.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());
// 列印内容 - 年齡從小到大 [JAVA8.User(name=周傳雄, phone=133, age=40), JAVA8.User(name=周星馳, phone=155, age=42), JAVA8.User(name=伍佰, phone=188, age=45), JAVA8.User(name=甄子丹, phone=199, age=45), JAVA8.User(name=周潤發, phone=166, age=52), JAVA8.User(name=劉德華, phone=177, age=62)]
log.info("年齡從小到大 " + ascUserList);
// 列印内容 - 年齡從大到小 [JAVA8.User(name=劉德華, phone=177, age=62), JAVA8.User(name=周潤發, phone=166, age=52), JAVA8.User(name=伍佰, phone=188, age=45), JAVA8.User(name=甄子丹, phone=199, age=45), JAVA8.User(name=周星馳, phone=155, age=42), JAVA8.User(name=周傳雄, phone=133, age=40)]
log.info("年齡從大到小 " + reversedUserList);
}
場景四:知道一個List對象,如何按照相同年齡進行分組,并擷取分組後的數量?
ini複制代碼public static void main(String[] args) {
// 知道一個List<User>對象,如何按照相同年齡進行分組?
List<User> userList = getUserList();
Map<Integer, List<User>> groupingUserList = userList.stream().collect(Collectors.groupingBy(User::getAge));
// 列印内容 - 相同年齡進行分組 {52=[JAVA8.User(name=周潤發, phone=166, age=52)], 40=[JAVA8.User(name=周傳雄, phone=133, age=40)], 42=[JAVA8.User(name=周星馳, phone=155, age=42)], 45=[JAVA8.User(name=伍佰, phone=188, age=45), JAVA8.User(name=甄子丹, phone=199, age=45)], 62=[JAVA8.User(name=劉德華, phone=177, age=62)]}
log.info("相同年齡進行分組 " + groupingUserList);
// 知道一個List<User>對象,如何按照相同年齡進行分組後擷取其對應數量?
Map<Integer, Long> countUserList = userList.stream().collect(Collectors.groupingBy(User::getAge, Collectors.counting()));
// 列印内容 - 相同年齡進行分組的數量 {52=1, 40=1, 42=1, 45=2, 62=1}
log.info("相同年齡進行分組的數量 " + countUserList);
}
1.2 進階
場景一:有一張使用者浏覽記錄表,一個使用者可以有多條浏覽記錄,且有不同的浏覽類型;比如小明浏覽娛樂子產品530秒,浏覽軍事子產品600秒,則對應兩個浏覽記錄資料。建立使用者浏覽類型統計表,根據現有的使用者浏覽記錄表資料對使用者浏覽類型統計表進行新增,請用Stream優雅的實作該功能?
java複制代碼package com.weige.javaskillpoint.controller;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
public class JAVA8 {
@Data
static class History {
// 使用者id
private Long userId;
// 使用者浏覽類型 (1->娛樂 2->軍事 3->教育)
private Integer type;
// 使用者浏覽時間(機關秒)
private Long seconds;
public History(Long userId, Integer type, Long seconds) {
this.userId = userId;
this.type = type;
this.seconds = seconds;
}
}
@Data
static class HistoryStatistics {
// 使用者id
private Long userId;
// 使用者浏覽類型 (1->娛樂 2->軍事 3->教育)
private Integer type;
// 浏覽類型統計數量
private Long count;
public HistoryStatistics(Long userId, Integer type, Long count) {
this.userId = userId;
this.type = type;
this.count = count;
}
}
public static void main(String[] args) {
// 模拟使用者浏覽記錄資料
List<History> historyList = new ArrayList<History>() {
{
add(new History(20231L, 1, 360L));
add(new History(20231L, 1, 720L));
add(new History(20231L, 2, 1360L));
add(new History(20231L, 2, 2360L));
add(new History(20239L, 2, 2360L));
add(new History(20239L, 3, 360L));
add(new History(20233L, 3, 360L));
}
};
List<HistoryStatistics> insertHistoryStatisticsList = new ArrayList<>();
// 根據使用者id進行分組
Map<Long, List<History>> groupingByUserIdMap = historyList.stream().collect(Collectors.groupingBy(History::getUserId));
groupingByUserIdMap.forEach((key, value) -> {
Map<Integer, Long> countMap = historyList.stream()
// 篩選出對應使用者id的浏覽記錄
.filter(h -> key.equals(h.getUserId()))
// 對使用者id的浏覽記錄根據類型進行分組并擷取數量
.collect(Collectors.groupingBy(History::getType, Collectors.counting()));
// 将使用者浏覽記錄類型分組的數量Map轉成List<HistoryStatistics>
List<HistoryStatistics> historyStatisticsList = countMap.entrySet().stream().map(u -> new HistoryStatistics(key, u.getKey(), u.getValue())).collect(Collectors.toList());
insertHistoryStatisticsList.addAll(historyStatisticsList);
});
// 批量新增使用者浏覽記錄統計表
batchInsertHistoryStatistics(insertHistoryStatisticsList);
}
public static void batchInsertHistoryStatistics(List<HistoryStatistics> insertHistoryStatisticsList) {
log.info("------連接配接資料庫------");
log.info("------開始批量新增資料------");
log.info("------批量新增資料: " + insertHistoryStatisticsList);
log.info("------批量新增資料結束------");
log.info("------關閉資料庫連接配接------");
}
}
場景二:有一張使用者浏覽記錄表,一個使用者可以有多條浏覽記錄,且有不同的浏覽類型;比如小明浏覽娛樂子產品530秒,浏覽軍事子產品600秒;小紅浏覽娛樂子產品1000秒,浏覽軍事子產品100秒,則對應四條浏覽記錄資料。想要得到每個使用者總共的浏覽時長,請用Stream優雅的實作該功能?
java複制代碼package com.weige.javaskillpoint.controller;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
public class JAVA8 {
@Data
static class History {
// 使用者姓名
private String userName;
// 使用者浏覽類型 (1->娛樂 2->軍事 3->教育)
private Integer type;
// 使用者浏覽時間(機關秒)
private Long seconds;
public History(String userName, Integer type, Long seconds) {
this.userName = userName;
this.type = type;
this.seconds = seconds;
}
}
public static void main(String[] args) {
List<History> historyList = new ArrayList<History>() {
{
add(new History("小明", 1, 360L));
add(new History("小明", 1, 720L));
add(new History("小明", 2, 1360L));
add(new History("小明", 2, 2360L));
add(new History("小紅", 2, 2360L));
add(new History("小白", 3, 360L));
add(new History("小紅", 3, 360L));
add(new History("小白", 3, 1060L));
}
};
// 串行流中reduce的第三個參數combiner無作用 傳回值可以寫為null
HashMap<String, Long> reduce = historyList.stream().reduce(new HashMap<>(), (m, e) -> {
m.put(e.getUserName(), m.getOrDefault(e.getUserName(), 0L) + e.getSeconds());
return m;
}, (m1,m2) -> null);
// 傳回結果 - {小明=4800, 小白=1420, 小紅=2720}
log.info("資料為: " + reduce);
// 并行流中reduce的第三個參數combiner有作用 如果傳回結果為map 則應用putAll()來解決并發情況下資料不一緻問題 同時傳回值應用ConcurrentHashMap接收
ConcurrentHashMap<String, Long> parallelReduce = historyList.stream().parallel().reduce(new ConcurrentHashMap<>(), (m, e) -> {
m.put(e.getUserName(), m.getOrDefault(e.getUserName(), 0L) + e.getSeconds());
return m;
}, (m1,m2) -> {
m1.putAll(m2);
return m1;
});
// 傳回結果 - {小明=4800, 小白=1420, 小紅=2720}
log.info("資料為: " + parallelReduce);
// 這裡舉個例子:如果reduce第一個參數為1,則stream執行時,分兩個階段;
// 第一個階段分3步:1 + 1 = 2,1 + 2 = 3,1 + 3 = 4;
// 第二個階段 2 * 3 * 4 = 24
List<Integer> intList = new ArrayList<Integer>(){{
add(1);
add(2);
add(3);
}};
Integer sum = intList.stream().parallel().reduce(1, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}, (integer, integer2) -> integer * integer2);
// 傳回結果 - 并行流中用第三個參數(類似于函數表達式對參數進行乘法操作): 24
log.info("并行流中用第三個參數(類似于函數表達式對參數進行乘法操作): " + sum);
Integer multipliers = intList.stream().parallel().reduce(1, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
});
// 傳回結果 - 并行流中第三個參數傳回自己本身,不進行任何操作: 9
log.info("并行流中第三個參數傳回自己本身,不進行任何操作: " + multipliers);
Integer num = intList.stream().reduce(1,Integer::sum);
// 傳回結果 - 串行流不使用第三個參數 7
log.info("串行流不使用第三個參數 " + num);
}
}
二. LocalDate,LocalDateTime
2.1 基礎
一般前端傳給後端的時間參數,都是字元串拼接,比如"2023-07-19","2023 -07-19 20:00:00",這都是字元串;而查詢資料庫時,是需要根據Date時間類型來查詢,是以這裡需要将字元串轉成Date,如果中間需要操作時間(年,月,日,時,分加減)
LocalDate
ini複制代碼 public static void main(String[] args) {
// 模拟前端傳過來的時間參數
String dateString = "2023-07-19";
// 将字元串轉成LocalDateTime 這裡看前端傳的時間格式 ofPattern裡面對應時間格式 不然會報錯
// 2023-07-19 -> yyyy-MM-dd
// LocalDate隻能操作年 月 日
LocalDate localDate = LocalDate.parse(dateString, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
// 減1年
LocalDate minusYears = localDate.minusYears(1);
// 加1年
LocalDate plusYears = localDate.plusYears(1);
// 減1月
LocalDate minusMonths = localDate.minusMonths(1);
// 加1月
LocalDate plusMonths = localDate.plusMonths(1);
// 減1日
LocalDate minusDays = localDate.minusDays(1);
// 加1日
LocalDate plusDays = localDate.plusDays(1);
// 通過LocalDate操作時間參數得到自己想要的結果時 轉換成Date類型查詢資料庫
// LocalDate轉Date
Date date = localDateTurnDate(minusYears);
getListByDate(date);
System.out.println(date);
}
public static Date localDateTurnDate(LocalDate localDate) {
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
return Date.from(instant);
}
public static void getListByDate(Date date) {
// 模拟查詢結果
ArrayList<String> arrayList = new ArrayList<String>() {{
add("一輩子");
add("下輩子");
}};
System.out.println("根據時間" + date + "查詢結果為 : " + arrayList);
}
LocalDateTime
ini複制代碼 public static void main(String[] args) {
// 模拟前端傳過來的時間參數
String dateString = "2023-07-19 20:20:20";
// 将字元串轉成LocalDateTime 這裡看前端傳的時間格式 ofPattern裡面對應時間格式 不然會報錯
// 2023-07-19 20:20:20 -> yyyy-MM-dd HH:mm:ss
// 2023-07-19 20:20 -> yyyy-MM-dd HH:mm
// 2023-07-19 -> yyyy-MM-dd
LocalDateTime localDateTime = LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 減1年
LocalDateTime minusYears = localDateTime.minusYears(1);
// 加1年
LocalDateTime plusYears = localDateTime.plusYears(1);
// 減1月
LocalDateTime minusMonths = localDateTime.minusMonths(1);
// 加1月
LocalDateTime plusMonths = localDateTime.plusMonths(1);
// 減1日
LocalDateTime minusDays = localDateTime.minusDays(1);
// 加1日
LocalDateTime plusDays = localDateTime.plusDays(1);
// 減1小時
LocalDateTime minusHours = localDateTime.minusHours(1);
// 加1小時
LocalDateTime plusHours = localDateTime.plusHours(1);
// 通過LocalDateTime操作時間參數得到自己想要的結果時 轉換成Date類型查詢資料庫
// LocalDateTime轉Date
Date date = localDateTimeTurnDate(minusYears);
getListByDate(date);
System.out.println(date);
}
public static Date localDateTimeTurnDate(LocalDateTime localDateTime) {
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
return Date.from(instant);
}
public static void getListByDate(Date date) {
// 模拟查詢結果
ArrayList<String> arrayList = new ArrayList<String>() {{
add("一輩子");
add("下輩子");
}};
System.out.println("根據時間" + date + "查詢結果為 : " + arrayList);
}
2.2 進階
對時間參數進行比較
通過目前時間,查詢前6個小時資料庫的資料
typescript複制代碼 public static void main(String[] args) {
// 近6個小時
String format = DateUtils.format(new Date(), "yyyy-MM-dd HH");
LocalDateTime now = LocalDateTime.parse(format, DateTimeFormatter.ofPattern("yyyy-MM-dd HH"));
for (LocalDateTime currentdate = now.minusHours(5); currentdate.isBefore(now) || currentdate.isEqual(now); currentdate = currentdate.plusHours(1)) {
Date date = localDateTimeTurnDate(currentdate);
getListByDate(date);
}
}
public static Date localDateTimeTurnDate(LocalDateTime localDateTime) {
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
return Date.from(instant);
}
public static void getListByDate(Date date) {
// 模拟查詢結果
ArrayList<String> arrayList = new ArrayList<String>() {{
add("一輩子");
add("下輩子");
}};
System.out.println("根據時間" + date + "查詢結果為 : " + arrayList);
}
通過目前時間,查詢近7天資料庫的資料
typescript複制代碼 public static void main(String[] args) {
// 近7天
String format = DateUtils.format(new Date(), "yyyy-MM-dd");
LocalDateTime now = LocalDate.parse(format, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay();
for (LocalDateTime currentdate = now.minusDays(6); currentdate.isBefore(now) || currentdate.isEqual(now); currentdate = currentdate.plusDays(1)) {
Date date = localDateTimeTurnDate(currentdate);
getListByDate(date);
}
}
public static Date localDateTimeTurnDate(LocalDateTime localDateTime) {
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
return Date.from(instant);
}
public static void getListByDate(Date date) {
// 模拟查詢結果
ArrayList<String> arrayList = new ArrayList<String>() {{
add("一輩子");
add("下輩子");
}};
System.out.println("根據時間" + date + "查詢結果為 : " + arrayList);
}