1、Stream流及API使用
1、stream的操作三步骤
-
创建Stream
一个数据源(如:集合、数组),获取一个流
-
中间操作
一个中间操作链,对数据源的数据进行处理
-
终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
2、筛选 / 切片
中间操作:
- filter:接收 Lambda ,从流中排除某些元素
- limit:截断流,使其元素不超过给定数量
- skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
- distinct:筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素

中间操作:不会执行任何操作
终止操作:一次性执行全部内容,即“惰性求值”。
2.1 filter:排除元素
@Test
public void test02(){
List<Employee> employees = getList();
Stream<Employee> employeeStream = employees.stream().filter((e -> {
System.out.println("中间操作");
return e.getAge() > 35;
}));
employeeStream.forEach(System.out::println);
}
内部迭代:迭代操作由Stream API完成
外部迭代:我们通过迭代器完成
// 外部迭代
@Test
public void test03(){
List<Employee> employees = getList();
Iterator<Employee> iterator = employees.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
2.2 短路,提高效率
短路:是找到想要的数据后就不在找,这样可提高遍历的效率
@Test
public void test05(){
List<Employee> employees = getList();
employees.stream().filter((e) -> {
System.out.println("短路!");
return e.getSalay() > 5000;
}).limit(2).forEach(System.out::println);
}
打印结果:

以上“短路”只打印了两次,说明只遍历了两次操作,找到想要的数据后,就不在继续向下遍历了,可大大提高遍历的效率。
2.3 distinct去重
public List<Employee> getList(){
List<Employee> employees = Arrays.asList(
new Employee("张三", 18, 8999.99),
new Employee("李四", 24, 6787.99),
new Employee("jack", 56, 3849.90),
new Employee("mark", 38, 7648.99),
new Employee("mark", 38, 7648.99),
new Employee("mark", 38, 7648.99),
new Employee("mark", 38, 7648.99));
return employees;
}
@Test
public void test06(){
List<Employee> employees = getList();
employees.stream().filter((e) -> e.getSalay() > 5000).distinct().forEach(System.out::println);
}
打印结果:

2.4 映射
- map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
@Test
public void test07(){
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
list.stream().map((str) -> str.toUpperCase()).forEach(System.out::println);
}
返回结果:
AAA
BBB
CCC
DDD
2.5 排序
-
sorted():自然排序
-
sorted(Comparator c):定制排序
/** * 自然排序 - sort * sorted() - 自然排序(Comparable) * sorted(Comparator com) - 定制排序 */ @Test public void test08(){ List<String> list = Arrays.asList("ccc", "aaa", "ddd", "bbb"); list.stream().sorted().forEach(System.out::println); System.out.println("-------------------------------"); List<Employee> employees = getList(); employees.stream().sorted((e1,e2) ->{ if(e1.getAge() == e2.getAge()){ return e1.getName().compareTo(e2.getName()); }else{ return e1.getAge().compareTo(e2.getAge()); } }).forEach(System.out::println); }打印结果:

2.6 查找 / 匹配
终止操作:
- allMatch:检查是否匹配所有元素
- anyMatch:检查是否至少匹配一个元素
- noneMatch:检查是否没有匹配所有元素
- findFirst:返回第一个元素
- findAny:返回当前流中的任意元素
- count:返回流中元素的总个数
- max:返回流中最大值
- min:返回流中最小值
1)检查相关操作
/**
* 检查相关操作
*/
@Test
public void test01(){
List<Employee> employees = getList();
boolean b = employees.stream().allMatch((e) -> e.getStatus().equals(Employee.Status.FREE));
System.out.println(b);
System.out.println("-------------------------");
boolean b1 = employees.stream().anyMatch((e) -> e.getStatus().equals(Employee.Status.FREE));
System.out.println(b1);
System.out.println("-------------------------");
Optional<Employee> op = employees.stream().sorted((e1,e2) -> Double.compare(e1.getSalay(),e2.getSalay())).findFirst();
System.out.println(op.get());
System.out.println("-------------------------");
Optional<Employee> employee = employees.stream()
.filter((e) -> e.getStatus().equals(Employee.Status.FREE))
.findAny();
System.out.println(employee);
}
2)汇总、最大值和最小值
/**
* 最大值和最小值
*/
@Test
public void test02(){
List<Employee> employees = getList();
long count = employees.stream().count();
System.out.println(count);
// 返回工资最大值的那条数据
System.out.println("--------------------");
Optional<Employee> employee = employees.stream().max((e1, e2) -> Double.compare(e1.getSalay(), e2.getSalay()));
System.out.println(employee.get());
System.out.println("--------------------");
// 获取最小工资数:先map找到所有的工资,再从这些里面找到工资数最小的那个值
Optional<Double> op = employees.stream().map(Employee::getSalay).min(Double::compare);
System.out.println(op.get());
}
2.7 规约 / 收集
1)reduce:可以将流中元素反复结合起来,得到一个值
/**
* 规约:
* reduce:可以将流中元素反复结合起来,得到一个值
*/
@Test
public void test01(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 累加的过程
Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(reduce);
System.out.println("----------------------------");
/**
* 计算员工集合中工资的总和
*/
List<Employee> employees = getList();
// 因为可能工资可能为空,所有返回的是Optional类型,防止空指针异常
Optional<Double> reduce1 = employees.stream().map(Employee::getSalay).reduce(Double::sum);
System.out.println(reduce1.get());
}
打印结果:

2)收集:collect将流转换成其他形式;接收一个Collector接口的实现,用于给流元素做汇总方法
@Test
public void test03(){
// 获取员工总数
List<Employee> employees = getList();
Long count = employees.stream().collect(Collectors.counting());
System.out.println(count);
// 平均值
Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalay));
System.out.println(avg);
// 总和
Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalay));
System.out.println(sum);
// 最大值
Optional<Employee> max = employees.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalay(), e2.getSalay())));
System.out.println(max.get());
// 最小值
Optional<Double> collect = employees.stream().map(Employee::getSalay).collect(Collectors.minBy(Double::compare));
System.out.println(collect.get());
}
打印结果:

2、Optional类
1、Optional类概念
Optional类(java.util.Optional)是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并可以避免空指针异常。
2、常用方法
- Optional.of(T t):创建一个 Optional 实例
- Optional.empty(T t):创建一个空的 Optional 实例
- Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则空实例
- isPresent():判断是否包含某值
- orElse(T t):如果调用对象包含值,返回该值,否则返回 t
- orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值
- map(Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional.empty()
- flatmap(Function mapper):与 map 相似,要求返回值必须是 Optional
1)of()方法:创建一个 Optional 实例
@Test
public void test01(){
Optional<Employee> optional = Optional.of(new Employee("张三"));
Employee employee = optional.get();
log.info("返回结果:{}", employee);
}
打印结果:

2)empty(T t)方法:创建一个空的 Optional 实例
@Test
public void test02(){
Optional<Employee> optional = Optional.empty();
log.info("返回结果:{}",optional.get());
}
3)isPresent():判断是否包含某值
@Test
public void test03(){
Optional<Employee> optional = Optional.ofNullable(new Employee());
if(optional.isPresent()){
log.info("返回结果:{}",optional.get());
}
}
4)orElse(T t):如果调用对象包含值,返回该值,否则返回 t
@Test
public void test04(){
Optional<Employee> optional = Optional.ofNullable(null);
if(optional.isPresent()){
log.info("返回结果:{}",optional.get());
}
Employee emp = optional.orElse(new Employee("张三", 28, 888.99, Employee.Status.FREE));
log.info("返回结果:{}",emp);
}
打印结果:

3、使用案例
1、Man类
@Data
public class Man {
private Optional<Godness> godness = Optional.empty();
public Man() {
}
public Man(Optional<Godness> godness) {
this.godness = godness;
}
}
2、Godness类
@Data
public class Godness {
private String name;
public Godness(String name) {
this.name = name;
}
}
3、寻找女神
以下代码的写法就可以避免空指针异常的情况
@Test
public void test05(){
Optional<Godness> godness = Optional.ofNullable(new Godness("aaa"));
Optional<Man> man = Optional.ofNullable(new Man(godness));
String godnessName = getGodnessName(man);
log.info("女神的名字:{}",godnessName);
}
//需求:获取一个男人心中女神的名字
// 如果传入的参数为null,则新创建一个对象
public String getGodnessName(Optional<Man> man){
return man.orElse(new Man())
.getGodness()
.orElse(new Godness("苍老师"))
.getName();
}
3、Date/Time API
3.1 本地时间/日期
-
LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。
注:ISO-8601日历系统是国际标准化组织指定的现代公民的日期和时间的表示法。
常用方法:
方法名 返回值类型 解释 now( ) static LocalDateTime 从默认时区的系统时钟获取当前日期 of(int year, int month, int dayOfMonth, int hour, int minute, int second) static LocalDateTime 从年,月,日,小时,分钟和秒获得 LocalDateTime的实例,将纳秒设置为零 plus(long amountToAdd, TemporalUnit unit) LocalDateTime 返回此日期时间的副本,并添加指定的数量 get(TemporalField field) int 从此日期时间获取指定字段的值为 int @Test public void test01(){ //获取当前时间日期 now LocalDateTime now = LocalDateTime.now(); log.info("当前时间:{}", now); // 2022-02-18T16:38:55.687 // 指定时间日期 of LocalDateTime of = LocalDateTime.of(2022, 02, 18, 16, 31, 24); log.info("指定时间日期:{}", of); // 2022-02-18T16:31:24 // 加 plus 指定时间日期加一天 LocalDateTime localDateTime = of.plusDays(1); log.info("指定时间日期+1天:{}", localDateTime); // 2022-02-19T16:31:24 // 减 minus 指定时间日期减一天 LocalDateTime minus = of.minusDays(1); log.info("指定时间日期-1天:{}", minus); // 2022-02-17T16:31:24 //获取指定的你年月日时分秒... get log.info("获取指定的你年月日时分秒:{}", now.getDayOfYear()); log.info("获取小时:{}", now.getHour()); log.info("获取秒钟:{}", now.getSecond()); log.info("获取月份:{}", now.getMonth()); }
3.2 时间/日期 差
- Duration:计算两个时间之间的间隔
- Period:计算两个日期之间的间隔
@Test
public void test03(){
//计算两个时间之间的间隔 between
Instant ins1 = Instant.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant ins2 = Instant.now();
Duration dura1 = Duration.between(ins1, ins2);
System.out.println(dura1.getSeconds());
System.out.println(dura1.toMillis());
}
@Test
public void test04(){
LocalDate ld1 = LocalDate.of(2016, 9, 1);
LocalDate ld2 = LocalDate.now();
Period period = Period.between(ld1, ld2); // ISO 标准
System.out.println(period.getYears());
System.out.println(period.toTotalMonths());
}
3.3 时间校正器

/**
* TimporalAdjuster:时间校正器
*/
@Test
public void test05(){
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
// 获取下一个周五的时间
LocalDateTime ldt2 = ldt.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println(ldt2);
// 自定义:下一个工作日
LocalDateTime ldt4 = ldt.with((l) -> {
LocalDateTime ldt3 = (LocalDateTime) l;
DayOfWeek week = ldt3.getDayOfWeek();
if (week.equals(DayOfWeek.FRIDAY)) {
return ldt3.plusDays(3);
} else if (week.equals(DayOfWeek.SATURDAY)) {
return ldt3.plusDays(2);
} else {
return ldt3.plusDays(1);
}
});
System.out.println("下一个工作日:" + ldt4);
}
3.4 时间格式化
- DateTimeFormatter:格式化时间 / 日期
/**
* DateTimeFormatter:格式化时间/日期
*/
@Test
public void test06(){
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE;
String format = dtf.format(now);
System.out.println(format); // 2022-02-18
System.out.println("-------------------------");
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String format2 = dateTimeFormatter.format(now);
System.out.println(format2); // 2022年02月18日 17:21:58
}
3.5 时区
- ZonedDate
- ZonedTime
- ZonedDateTime
@Test
public void test02(){
//查看支持的时区
Set<String> set = ZoneId.getAvailableZoneIds();
set.forEach(System.out::println);
//指定时区
LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
System.out.println(ldt1);
//在已构建好的日期时间上指定时区
LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
ZonedDateTime zdt1 = ldt2.atZone(ZoneId.of("Europe/Tallinn"));
System.out.println(zdt1);
}
3.6 Date与LocalDateTime之间的转换
- 原则:利用时间戳Instant
@Test
public void test03(){
// Date 转 LocalDateTime
Date date = new Date();
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();
// LocalDateTime 转 Date
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zdt = localDateTime.atZone(zoneId);
Date date = Date.from(zdt.toInstant());
}
5770

被折叠的 条评论
为什么被折叠?



