6. Stream API
6.1 概念
什么是 Stream?
6.2
Stream的操作步骤:
6.3 创建流
创建流:几种方法如下
/**
* 创建流
*/
@Test
public void test01(){
/**
* 集合流
* - Collection.stream() 穿行流
* - Collection.parallelStream() 并行流
*/
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//数组流
//Arrays.stream(array)
String[] strings = new String[10];
Stream<String> stream2 = Arrays.stream(strings);
//Stream 静态方法
//Stream.of(...)
Stream<Integer> stream3 = Stream.of(1, 2, 3);
//无限流
//迭代
Stream<Integer> iterate = Stream.iterate(0, (i) -> i + 2);
iterate.forEach(System.out::println);
//生成
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
6.4 筛选 / 切片
中间操作:
- filter:接收 Lambda ,从流中排除某些元素
- limit:截断流,使其元素不超过给定数量
- skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
- distinct:筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
@Test
public void test01(){
emps.stream()
.filter((x) -> x.getAge() > 35)
.limit(3) //短路?达到满足不再内部迭代
.distinct()
.skip(1)
.forEach(System.out::println);
}
Stream的中间操作:
- 内部迭代:迭代操作由 Stream API 完成
- 外部迭代:我们通过迭代器完成
6.5 映射
- map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
map:
@Test
public void test02(){
List<String> list = Arrays.asList("a", "b", "c");
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
}
@Test
public void test03(){
emps.stream()
.map(Employee::getName)
.forEach(System.out::println);
}
flatMap:
public Stream<Character> filterCharacter(String str){
List<Character> list=new ArrayList<>();
for (char c:str.toCharArray()){
list.add(c);
}
return list.stream();
}
@Test
public void test05(){
List<String> list=Arrays.asList("aaa","bbb","ccc");
Demo01 demo=new Demo01();
list.stream().
flatMap(demo::filterCharacter).
forEach(System.out::println);
}
6.6 排序
- sorted():自然排序
- sorted(Comparator c):定制排序
Comparable:自然排序
@Test
public void test04(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
list.stream()
.sorted() //comparaTo()
.forEach(System.out::println);
}
Comparator:定制排序
@Test
public void test05(){
emps.stream()
.sorted((e1, e2) -> { //compara()
if (e1.getAge().equals(e2.getAge())){
return e1.getName().compareTo(e2.getName());
} else {
return e1.getAge().compareTo(e2.getAge());
}
})
.forEach(System.out::println);
}
6.7 查找 / 匹配
终止操作:
- allMatch:检查是否匹配所有元素
- anyMatch:检查是否至少匹配一个元素
- noneMatch:检查是否没有匹配所有元素
- findFirst:返回第一个元素
- findAny:返回当前流中的任意元素
- count:返回流中元素的总个数
- max:返回流中最大值
- min:返回流中最小值
注意:只有终止操作存在之后,中间操作才会执行
如果没有写终止操作,只执行中间操作,则不会得到结果
//3. 终止操作
/*
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
*/
public enum Status {
FREE, BUSY, VOCATION;
}
@Test
public void test01(){
List<Status> list = Arrays.asList(Status.FREE, Status.BUSY, Status.VOCATION);
boolean flag1 = list.stream()
.allMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag1);
boolean flag2 = list.stream()
.anyMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag2);
boolean flag3 = list.stream()
.noneMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag3);
// 避免空指针异常
Optional<Status> op1 = list.stream()
.findFirst();
// 如果Optional为空 找一个替代的对象
Status s1 = op1.orElse(Status.BUSY);
System.out.println(s1);
Optional<Status> op2 = list.stream()
.findAny();
System.out.println(op2);
long count = list.stream()
.count();
System.out.println(count);
}
6.8 归约 / 收集
- 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
- 收集:collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
reduce:
/**
* Java:
* - reduce:需提供默认值(初始值)
* Kotlin:
* - fold:不需要默认值(初始值)
* - reduce:需提供默认值(初始值)
*/
@Test
public void test01(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Integer integer = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(integer);
}
collect:
List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
@Test
public void test02(){
//放入List
List<String> list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
//放入Set
Set<String> set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
//放入LinkedHashSet
LinkedHashSet<String> linkedHashSet = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(LinkedHashSet::new));
linkedHashSet.forEach(System.out::println);
}
@Test
public void test03(){
//总数
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
//平均值
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
//总和
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
//最大值
Optional<Employee> max = emps.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(max.get());
//最小值
Optional<Double> min = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println(min.get());
}
@Test
public void test04(){
//分组
Map<Integer, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getId));
System.out.println(map);
//多级分组
Map<Integer, Map<String, List<Employee>>> mapMap = emps.stream()
.collect(Collectors.groupingBy(Employee::getId, Collectors.groupingBy((e) -> {
if (e.getAge() > 35) {
return "开除";
} else {
return "继续加班";
}
})));
System.out.println(mapMap);
//分区
Map<Boolean, List<Employee>> listMap = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() > 4321));
System.out.println(listMap);
}
@Test
public void test05(){
//总结
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
System.out.println(dss.getMin());
System.out.println(dss.getSum());
System.out.println(dss.getCount());
System.out.println(dss.getAverage());
//连接
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("-")); //可传入分隔符
System.out.println(str);
}
6.9 案例
**案例一:**给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?(如:给定【1,2,3,4,5】,返回【1,4,9,16,25】)
@Test
public void test01(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream()
.map((x) -> x * x)
.forEach(System.out::println);
}
**案例二:**怎样使用 map 和 reduce 数一数流中有多少个 Employee 呢?
List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
@Test
public void test02(){
Optional<Integer> result = emps.stream()
.map((e) -> 1)
.reduce(Integer::sum);
System.out.println(result.get());
6.10 并行流
- 并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流
- Java 8 中将并行进行了优化,我们可以很容易的对数据进行操作;Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流之间切换
Fork / Join 框架:
Fork / Join 实现:
public class ForkJoinCalculate extends RecursiveTask<Long> {
private static final long serialVersionUID = 1234567890L;
private long start;
private long end;
private static final long THRESHPLD = 10000;
public ForkJoinCalculate(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end - start;
if (length <= THRESHPLD) {
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
} else {
long middle = (start + end) / 2;
ForkJoinCalculate left = new ForkJoinCalculate(start, end);
left.fork(); //拆分子任务 压入线程队列
ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
right.fork();
return left.join() + right.join();
}
return null;
}
}
public class TestForkJoin {
/**
* ForkJoin 框架
*/
@Test
public void test01(){
Instant start = Instant.now();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinCalculate task = new ForkJoinCalculate(0, 100000000L);
Long sum = pool.invoke(task);
System.out.println(sum);
Instant end = Instant.now();
System.out.println(Duration.between(start, end).getNano());
}
/**
* 普通 for循环
*/
@Test
public void test02(){
Instant start = Instant.now();
Long sum = 0L;
for (long i = 0; i < 100000000L; i++) {
sum += i;
}
Instant end = Instant.now();
System.out.println(Duration.between(start, end).getNano());
}
}
Java 8 并行流 / 串行流:
@Test
public void test03(){
//串行流(单线程):切换为并行流 parallel()
//并行流:切换为串行流 sequential()
LongStream.rangeClosed(0, 100000000L)
.parallel() //底层:ForkJoin
.reduce(0, Long::sum);
}
7. Optional
**定义:**Optional 类 (java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在用 Optional 可以更好的表达这个概念;并且可以避免空指针异常
常用方法:
- 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
/*
* 一、Optional 容器类:用于尽量避免空指针异常
* Optional.of(T t) : 创建一个 Optional 实例
* Optional.empty() : 创建一个空的 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
*/
Optional.of(T t):
@Test
public void test01(){
Optional<Employee> op = Optional.of(new Employee());
Employee employee = op.get();
}
Optional.empty(T t):
@Test
public void test02(){
Optional<Employee> op = Optional.empty();
Employee employee = op.get();
}
Optional.ofNullable(T t):
@Test
public void test03(){
Optional<Employee> op = Optional.ofNullable(new Employee());
Employee employee = op.get();
}
isPresent():
@Test
public void test03(){
Optional<Employee> op = Optional.ofNullable(new Employee());
if (op.isPresent()) {
Employee employee = op.get();
}
}
orElse(T t)
orElseGet(Supplier s)
@Test
public void test3(){
Optional<Employee> op = Optional.ofNullable(new Employee());
if(op.isPresent()){
System.out.println(op.get());
}
Employee emp = op.orElse(new Employee("张三"));
System.out.println(emp);
Employee emp2 = op.orElseGet(() -> new Employee());
System.out.println(emp2);
}
map(Function f)
flatMap(Function mapper)
@Test
public void test4(){
Optional<Employee> op = Optional.of(new Employee(101, "张三", 18, 9999.99));
Optional<String> op2 = op.map(Employee::getName);
System.out.println(op2.get());
Optional<String> op3 = op.flatMap((e) -> Optional.of(e.getName()));
System.out.println(op3.get());
}
8. 接口
8.0 概念
8.1 默认方法
public interface MyFun {
default String getName(){
return "libo";
}
default Integer getAge(){
return 22;
}
}
类优先原则:
public interface MyFun {
default String getName(){
return "哈哈哈";
}
}
public class MyClass {
public String getName(){
return "嘿嘿嘿";
}
}
public class SubClass extends MyClass implements MyFun{
}
public class TestDefaultInterface {
public static void main(String[] args) {
SubClass sc = new SubClass();
System.out.println(sc.getName());
}
}
8.2 静态方法
public interface MyFun {
static void getAddr(){
System.out.println("addr");
}
static String Hello(){
return "Hello World";
}
}
9. Date / Time API
9.0 概念
9.1 安全问题
传统的日期格式化:
@Test
public void test01(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Callable<Date> task = () -> sdf.parse("20200517");
ExecutorService pool = Executors.newFixedThreadPool(10);
ArrayList<Future<Date>> result = new ArrayList<>();
for (int i = 0; i < 10; i++) {
result.add(pool.submit(task));
}
for (Future<Date> future : result) {
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
pool.shutdown();
}
报出异常
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.example.learnDataAPI.main(learnDataAPI.java:37)
Caused by: java.lang.NumberFormatException: multiple points
...
解决方法:加锁
public class DateFormatThreadLocal {
private static final ThreadLocal<DateFormat> df = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static Date convert(String source) throws ParseException{
return df.get().parse(source);
}
}
@Test
public void test02(){
Callable<Date> task = () -> DateFormatThreadLocal.convert("20200517");
ExecutorService pool = Executors.newFixedThreadPool(10);
ArrayList<Future<Date>> result = new ArrayList<>();
for (int i = 0; i < 10; i++) {
result.add(pool.submit(task));
}
for (Future<Date> future : result) {
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
pool.shutdown();
}
DateTimeFormatter:
@Test
public void test03(){
DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
Callable<LocalDate> task = () -> LocalDate.parse("20200517",dtf);
ExecutorService pool = Executors.newFixedThreadPool(10);
ArrayList<Future<LocalDate>> result = new ArrayList<>();
for (int i = 0; i < 10; i++) {
result.add(pool.submit(task));
}
for (Future<LocalDate> future : result) {
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
pool.shutdown();
}
9.2 本地时间 / 日期
ISO 标准:
常用方法:
方法名 | 返回值类型 | 解释 |
---|---|---|
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 ldt1 = LocalDateTime.now();
System.out.println(ldt1);
//指定时间日期 of
LocalDateTime ldt2 = LocalDateTime.of(2020, 05, 17, 16, 24, 33);
System.out.println(ldt2);
//加 plus
LocalDateTime ldt3 = ldt2.plusYears(2);
System.out.println(ldt3);
//减 minus
LocalDateTime ldt4 = ldt2.minusMonths(3);
System.out.println(ldt4);
//获取指定的你年月日时分秒... get
System.out.println(ldt2.getDayOfYear());
System.out.println(ldt2.getHour());
System.out.println(ldt2.getSecond());
}
LocalDate / LocalTime 不再一一例举…
9.3 时间戳
Instant:以 Unix 元年 1970-01-01 00:00:00 到某个时间之间的毫秒值
@Test:
@Test
public void test02(){
// 默认获取 UTC 时区 (UTC:世界协调时间)
Instant ins1 = Instant.now();
System.out.println(ins1);
//带偏移量的时间日期 (如:UTC + 8)
OffsetDateTime odt1 = ins1.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt1);
//转换成对应的毫秒值
long milli1 = ins1.toEpochMilli();
System.out.println(milli1);
//构建时间戳
Instant ins2 = Instant.ofEpochSecond(60);
System.out.println(ins2);
}
9.4 时间 / 日期 差
- Duration:计算两个时间之间的间隔
- Period:计算两个日期之间的间隔
@Test:
@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());
}
9.5 时间校正器
操纵日期:
@Test:
@Test
public void test01(){
//TemporalAdjusters:时间校正器
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println(ldt1);
//指定日期时间中的 年 月 日 ...
LocalDateTime ldt2 = ldt1.withDayOfMonth(10);
System.out.println(ldt2);
//指定时间校正器
LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
//自定义时间校正器
LocalDateTime ldt5 = ldt1.with((ta) -> {
LocalDateTime ldt4 = (LocalDateTime) ta;
DayOfWeek dow1 = ldt4.getDayOfWeek();
if (dow1.equals(DayOfWeek.FRIDAY)) {
return ldt4.plusDays(3);
} else if (dow1.equals(DayOfWeek.SATURDAY)) {
return ldt4.plusDays(2);
} else {
return ldt4.plusDays(1);
}
});
System.out.println(ldt5);
}
9.6 格式化
- DateTimeFormatter:格式化时间 / 日期
@Test
public void test01(){
//默认格式化
DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE_TIME;
LocalDateTime ldt1 = LocalDateTime.now();
String str1 = ldt1.format(dtf1);
System.out.println(str1);
//自定义格式化 ofPattern
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt2 = LocalDateTime.now();
String str2 = ldt2.format(dtf2);
System.out.println(str2);
//解析
LocalDateTime newDate = ldt1.parse(str1, dtf1);
System.out.println(newDate);
}
9.7 时区
- ZonedDate
- ZonedTime
- ZonedDateTime
@Test:
@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);
}
一些转换:
@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());
// 原则:利用 时间戳Instant
}
10. 注解
10.0 概念
10.1 重复注解
定义注解:
@Repeatable(MyAnnotations.class) //指定容器类
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "Java 8";
}
定义容器:
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
@Test:
public class Test01 {
//重复注解
@Test
@MyAnnotation("Hello")
@MyAnnotation("World")
public void test01() throws NoSuchMethodException {
Class<Test01> clazz = Test01.class;
Method test01 = clazz.getMethod("test01");
MyAnnotation[] mas = test01.getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation ma : mas) {
System.out.println(ma.value());
}
}
}
10.2 类型注解
Java 8 新增注解:新增ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)