尚硅谷java8新特性视频连接
主要内容:
Lambda 表达式
函数式接口
方法引用与构造器引用
Stream API
接口中的默认方法与静态方法
新时间日期 API
其他新特性
HashMap等其他特性这里不做赘述。
1、Lambda 表达式
1.1、为什么使用 Lambda 表达式
1.2、 匿名内部类
@Test
public void test01(){
//匿名内部类
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
@Override
public boolean equals(Object obj) {
return false;
}
};
//调用
TreeSet<Integer> set = new TreeSet<>(comparator);
}
1.3、使用Lambda
@Test
public void test02(){
// Lambda 表达式
Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
TreeSet<Integer> set = new TreeSet<>(comparator);
}
1.4、Lambda、Stream API尝尝鲜
//定义Employee
public class Employee {
private int id;
private String name;
private int age;
private double salary;
private Status status;
//提供get、set方法、有参无参构造、tostring。。。。。。
}
//定义接口。如果这个接口被@FunctionalInterface修饰,那么它只能有一个抽象方法
@FunctionalInterface
public interface MyPredicate<T> {
public boolean test(T t);
}
public class Test001 {
//准备数据
List<Employee> emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
//定义过滤方法
public List<Employee> filterEmployee(List<Employee> emps, MyPredicate<Employee> mp){
List<Employee> list = new ArrayList<>();
for (Employee employee : emps) {
if(mp.test(employee)){
list.add(employee);
}
}
return list;
}
//采用匿名内部类,查询id小于103的
@Test
public void test5(){
List<Employee> list = filterEmployee(emps, new MyPredicate<Employee>() {
@Override
public boolean test(Employee t) {
return t.getId() <= 103;
}
});
for (Employee employee : list) {
System.out.println(employee);
}
}
//采用Lambda 表达式
@Test
public void test6(){
//查找年龄小于35的集合
List<Employee> list = filterEmployee(emps, (e) -> e.getAge() <= 35);
list.forEach(System.out::println);
System.out.println("------------------------------------------");
//查找大于5000的集合
List<Employee> list2 = filterEmployee(emps, (e) -> e.getSalary() >= 5000);
list2.forEach(System.out::println);
}
@Test
public void test7(){
//查找年龄小于等于35的集合
emps.stream()
.filter((e) -> e.getAge() <= 35)
.forEach(System.out::println);
System.out.println("----------------------------------------------");
//取出集合中3个人的名字,并输出
emps.stream()
.map(Employee::getName)
.limit(3)
.sorted()
.forEach(System.out::println);
}
}
2、Lambda基础语法
2.1、Lambda 表达式的基础语法:
操作符“->”
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体
-
无参数,无返回值 () -> System.out.println("Hello Lambda!");
@Test
public void test1(){
int num = 0;//jdk 1.7 前,必须是 final
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!" + num);
}
};
r.run();
System.out.println("-------------------------------");
Runnable r1 = () -> System.out.println("Hello Lambda!");
r1.run();
}
-
有一个参数,并且无返回值(若只有一个参数,小括号可以省略不写) (x) -> System.out.println(x)
@Test
public void test2(){
Consumer<String> con = x -> System.out.println(x);
con.accept("对方是否说");
}
-
有两个以上的参数,有返回值,并且 Lambda 体中有多条语句(多条语句加{}大括号)
@Test
public void test04(){
Comparator<Integer> comparator = (x, y) -> {
System.out.println("测试比较接口");
return Integer.compare(x, y);
};
}
若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
@Test
public void test04(){
Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
}
2.3、函数式接口
函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。 可以使用注解 @FunctionalInterface 修饰
![](https://i-blog.csdnimg.cn/blog_migrate/36e9efcf8a7092cc9a408a40b5c85c3b.png)
2.4、案例
1:调用 Collections.sort() 方法,通过定制排序 比较两个 Employee (先按照年龄比,年龄相同按照姓名比),使用 Lambda 表达式作为参数传递
@Test
public void test01(){
Collections.sort(emps, (e1, e2) -> {
if (e1.getAge() == e2.getAge()){
return e1.getName().compareTo(e2.getName());
} else {
return Integer.compare(e1.getAge(), e2.getAge());
}
});
for (Employee emp : emps) {
System.out.println(emp);
}
}
//声明接口
@FunctionalInterface
public interface MyFunction {
public String getValue(String str);
}
//定义方法
public String strHandler(String str, MyFunction mf){
return mf.getValue(str);
}
//测试,调用方法
@Test
public void test2(){
String trimStr = strHandler("\t\t\t 去除空格 ", (str) -> str.trim());
System.out.println(trimStr);
String upper = strHandler("abcdef", (str) -> str.toUpperCase());
System.out.println(upper);
String newStr = strHandler("截取字符串", (str) -> str.substring(2, 5));
System.out.println(newStr);
}
3、声明一个带两个泛型的函数式接口,泛型类型为<T, R> T 为参数,R 为返回值;接口中声明对应的抽象方法;在 TestLambda 类中声明方法,使用接口作为参数,计算两个 Long 类型参数的和;在计算两个 Long 类型参数的乘积
//声明接口
public interface MyFunction2<T, R> {
public R getValue(T t1, T t2);
}
//需求:对于两个 Long 型数据进行处理
public void op(Long l1, Long l2, MyFunction2<Long, Long> mf){
System.out.println(mf.getValue(l1, l2));
}
//测试调用
@Test
public void test3(){
op(100L, 200L, (x, y) -> x + y);
op(100L, 200L, (x, y) -> x * y);
}
3、函数式接口(java8内置)
Java内置四大核心函数式接口:
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 | T | void | 对类型为T的对象应用操作:void accept(T t) |
Supplier 提供型接口 | 无 | T | 返回类型为T的对象:T get() |
Function<T, R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果为R类型的对象:R apply(T t) |
Predicate 断言型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值:boolean test(T t) |
3.1、Consumer消费性接口
@Test
public void test01(){
Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x);
//传入参数,调用consumer方法
consumer.accept(100);
}
3.2、Supplier提供型接口
@Test
public void test07(){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Supplier<Integer> supplier = ()->(int)(Math.random() * 10);
list.add(supplier.get());
}
for (Integer integer : list) {
System.out.println(integer);
}
}
3.3、Function<T, R> 函数型接口
@Test
public void test03(){
//Function<T, R>
String oldStr = "abc123456xyz";
Function<String, String> function = (s) -> s.substring(1, s.length()-1);
//test
System.out.println(function.apply(oldStr));
}
3.4、Predicate<T> 断言型接口
@Test
public void test08(){
Integer age = 35;
Predicate<Integer> predicate = (i) -> i >=10;
if (predicate.test(age)){//满足上面条件的
System.out.println("你满足条件啊--->"+age);
} else {//不满足上面条件的
System.out.println("抱歉,你不满足大于等于10的条件--->"+age);
}
}
3.5、其他接口
4、方法引用
语法格式:
- 对象 :: 实例方法
- 类 :: 静态方法
- 类 :: 实例方法
4.1、对象 :: 实例方法
@Test
public void test01(){
//常规lambda写法
PrintStream ps = System.out;
Consumer<String> con1 = (s) -> ps.println(s);
con1.accept("aaa");
//对象::实例写法
Consumer<String> con2 = System.out::println;
con2.accept("bbb");
}
注意:Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致
![](https://i-blog.csdnimg.cn/blog_migrate/921583825492aa70fee1ba6287aaa68d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4b07f6ddd3d78a27b96bbde859bfcd21.png)
4.2、类::静态方法
@Test
public void test02(){
//常规写法
Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
System.out.println(com1.compare(1, 2));
//方法引用写法
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(2, 1));
}
4.3、类 :: 实例方法
@Test
public void test03(){
BiPredicate<String, String> bp1 = (x, y) -> x.equals(y);
System.out.println(bp1.test("a","b"));
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("c","c"));
}
条件:Lambda 参数列表中的第一个参数是方法的调用者,第二个参数是方法的参数时,才能使用 ClassName :: Method
解释:以上面代码为例,x为调用者,y为方法的参数
4.4、构造器引用
格式:
- ClassName :: new
@Test
public void test6(){
Supplier<Employee> sup = () -> new Employee();
System.out.println(sup.get());
System.out.println("------------------------------------");
Supplier<Employee> sup2 = Employee::new;//直接对应的是无参构造器
System.out.println(sup2.get());
}
注意:需要调用的构造器的参数列表要与函数时接口中抽象方法的参数列表保持一致
4.5、数组引用
语法:
- Type :: new;
@Test
public void test8(){
Function<Integer, String[]> fun = (args) -> new String[args];
String[] strs = fun.apply(10);
System.out.println(strs.length);
System.out.println("--------------------------");
Function<Integer, String[]> fun2 = String[] :: new;
String[] st2 = fun2.apply(20);
System.out.println(st2 .length);
}
5、Stream API
5.1、什么是 Stream
流(Stream) 到底是什么呢? 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
5.2、Stream 的操作三个步骤
创建 Stream
一个数据源(如:集合、数组),获取一个流
中间操作
一个中间操作链,对数据源的数据进行处理
终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
5.3、创建流
/**
* 创建流
*/
@Test
public void test01(){
/**
* 集合流
* - Collection.stream() 穿行流
* - Collection.parallelStream() 并行流
*/
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//通过Arrays中的静态方法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> stream4 = Stream.iterate(0, (i) -> i+2);
stream4.forEach(System.out::println);
//生成
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
5.4中间操作---筛选与切片
filter——接收 Lambda , 从流中排除某些元素。
limit——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
//准备数据
List<Employee> emps = Arrays.asList(
new Employee(101, "aa", 19, 9999.99),
new Employee(102, "bb", 20, 7777.77),
new Employee(103, "cc", 35, 6666.66),
new Employee(104, "dd", 44, 1111.11),
new Employee(105, "ff", 60, 4444.44)
);
//测试中间操作
@Test
public void test01(){
emps.stream()
.filter((x) -> x.getAge() > 35) //filter满足条件
.limit(3) //截取
.distinct()//去重复
.skip(1)//跳过筛选出来的第一个元素
//forEach算是终止操作,只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
.forEach(System.out::println);
}
“惰性求值”测试
/**
* --------惰性求值-------
* 当注释掉forEach打印时,运行不会打印“测试中间操作”
* 当放开forEach,运行就会打印“测试中间操作”。
* 说明只有当走到终止操作试,所有的中间操作才会执行。这就叫“惰性求值”,又称“延迟加载”
* --------内部迭代-------
* 运行下面代码,“测试中间操作”这个会打印很多次,数据符合一次打印一次
* 就如同迭代一样。而这个迭代是Stream API给我们完成的。称为“内部迭代”
*/
@Test
public void test2(){
//所有的中间操作不会做任何的处理
Stream<Employee> stream = emps.stream()
.filter((e) -> {
System.out.println("测试中间操作");
return e.getAge() <= 35;
});
//只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
stream.forEach(System.out::println);
}
5.5、中间操作---映射
map——接收 Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
map:
@Test
public void test1(){
//将集合中的字母转成大写
List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
strList.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
}
flatMap:
//组装数据,并返回stream流
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 test1(){
//map本身会返回一个stream流,而调用的filterCharacter方法也返回一个stream流.这样返回的结果就是Stream<Stream<Character>>
Stream<Stream<Character>> stream2 = strList.stream()
.map(TestStreamAPI1::filterCharacter);
//必须steam遍历,取出还是stream,然后再遍历
stream2.forEach((sm) -> {
sm.forEach(System.out::println);
});
System.out.println("---------------------------------------------");
//flatMap方法,会将每个值都换成流,然后在汇聚成一个流,这样调用跟map同样的方法,返回Stream<Character>
Stream<Character> stream3 = strList.stream().flatMap(TestStreamAPI1::filterCharacter);
//用flatmap接收,会将一个个的流,汇成一个流。只需遍历一次
stream3.forEach(System.out::println);
}
5.6、中间操作---排序
- sorted():自然排序
- sorted(Comparator c):定制排序
sorted:自然排序
@Test
public void test04(){
List<Integer> list = Arrays.asList(2,1,3,6,4,5);
list.stream()
.sorted() //comparaTo()
.forEach(System.out::println);
}
sorted(Comparator c):定制排序
@Test
public void test2(){
emps.stream()
.sorted((x, y) -> {
if(x.getAge() == y.getAge()){//年龄相同用名字排序
return x.getName().compareTo(y.getName());
}else{//年龄不同,用年龄排
return Integer.compare(x.getAge(), y.getAge());
}
}).forEach(System.out::println);
}
5.7、终止操作---查找 / 匹配
- allMatch:检查是否匹配所有元素
- anyMatch:检查是否至少匹配一个元素
- noneMatch:检查是否没有匹配所有元素
- findFirst:返回第一个元素
- findAny:返回当前流中的任意元素
- count:返回流中元素的总个数
- max:返回流中最大值
- min:返回流中最小值
@Test
public void test1(){
boolean bl = emps.stream()
.allMatch((e) -> e.getStatus().equals(Status.BUSY));//allMatch-->所有人的状态是不是busy,如果有一个不是,那么就返回false
System.out.println(bl);
boolean bl1 = emps.stream()
.anyMatch((e) -> e.getStatus().equals(Status.BUSY));//anyMatch-->只要有一个人是busy,就返回true
System.out.println(bl1);
boolean bl2 = emps.stream()
.noneMatch((e) -> e.getStatus().equals(Status.BUSY));//noneMatch-->如果都没有busy,才返回true。
System.out.println(bl2);
}
findFirst和findAny(并阐述Optional作用):
/**
* Optional java8带的新特性,如果返回的值有可能为null时,就会将这个返回值,封装到Optional里。
*/
@Test
public void test2(){
//寻找集合中,工资最低的
Optional<Employee> op = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
//如果返回值,真为null了,那么就返回你设置的orElse里面的值。
op.orElse(new Employee(102, "李四", 59, 6666.66, Status.BUSY));
System.out.println(op.get());
System.out.println("--------------------------------");
//寻找集合中任意一个状态是free的
Optional<Employee> op2 = emps.parallelStream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.findAny();
System.out.println(op2.get());
}
count、max、min测试
@Test
public void test3(){
//状态为free的个数
long count = emps.stream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.count();
System.out.println(count);
//获取最大的钱数
Optional<Double> op = emps.stream()
.map(Employee::getSalary)
.max(Double::compare);
System.out.println(op.get());
//获取最小钱数的人
Optional<Employee> op2 = emps.stream()
.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op2.get());
}
5.8、终止操作---归约 / 收集
- 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
简单版归约
@Test
public void test1(){
//将集合中的数加起来得出最终sum
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);//以0为起始,0+1,然后赋值给x,y再从集合中取出2,然后2+1,依次递归进行
System.out.println(sum);
}
求出对象中的总值:
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 79, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
@Test
public void test1(){
//第一种:求出集合中工资总数
Optional<Double> op = emps.stream()
.map(Employee::getSalary)//将集合中的工资全部放到map中。
.reduce(Double::sum);//然后reduce求出最后总值
System.out.println(op.get());
//第二种:求出集合中工资总数
Stream<Double> stream = emps.stream()
.map(Employee::getSalary);//将集合中的工资全部放到map中。
Double reduce = stream.reduce((0.0), (x, y) -> x + y);//然后reduce求出最后总值
System.out.println(reduce);
}
5.9、终止操作--- 收集
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 79, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
@Test
public void test3(){
List<String> list = emps.stream()
.map(Employee::getName) //收集集合中所有名字
.collect(Collectors.toList());//将收集的名字转成list(Collectors.toList())
list.forEach(System.out::println);
System.out.println("----------------------------------");
Set<String> set = emps.stream()
.map(Employee::getName)//收集集合中所有名字
.collect(Collectors.toSet());//将收集的名字转成set(Collectors.toSet())
set.forEach(System.out::println);
System.out.println("----------------------------------");
HashSet<String> hs = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));//将收集的名字放到HashSet中
hs.forEach(System.out::println);
}
求和、平均值、总数、最大值、最小值
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 79, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
@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> max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
//最小值
Optional<Double> min = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println(min.get());
}
求和、平均值、总数、最大值、最小值进阶版
/**
* 只需调用Collectors.summarizingDouble。通过这个返回值,调用方法。
* summarizingDouble返回是double
* summarizingInt返回是int类型
* summarizingLong返回是long类型
*/
@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);
}
连接
@Test
public void test05(){
//将集合中的名字连接
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("-")); //可传入分隔符
System.out.println(str);
}
分组
//分组 按照状态分组
@Test
public void test5(){
Map<Status, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
}
多级分组
@Test
public void test6(){
Map<Status, Map<String, List<Employee>>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if(e.getAge() >= 60)
return "老年";
else if(e.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(map);
}
分区
@Test
public void test7(){
//满足大于5000工资的一个区,不满足的一个区
Map<Boolean, List<Double>> map = emps.stream().map(Employee::getSalary)
.collect(Collectors.partitioningBy((e) -> e >= 5000));
System.out.println(map);
//如果是对象的话,List里面值为对象,也不用map去获取各个流了
Map<Boolean, List<Employee>> map2 = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
System.out.println(map2);
}
5.10案例练习
案例一:**给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?(如:给定【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);
}
案例二:
public class TestTransaction {
List<Transaction> transactions = null;
@Before
public void before(){
Trader raoul = new Trader("Raoul", "Cambridge");
Trader mario = new Trader("Mario", "Milan");
Trader alan = new Trader("Alan", "Cambridge");
Trader brian = new Trader("Brian", "Cambridge");
transactions = Arrays.asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950)
);
}
//1. 找出2011年发生的所有交易, 并按交易额排序(从低到高)
@Test
public void test1(){
transactions.stream()
.filter((t) -> t.getYear() == 2011)
.sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
.forEach(System.out::println);
}
//2. 交易员都在哪些不同的城市工作过?
@Test
public void test2(){
transactions.stream()
.map((t) -> t.getTrader().getCity())
.distinct()
.forEach(System.out::println);
}
//3. 查找所有来自剑桥的交易员,并按姓名排序
@Test
public void test3(){
transactions.stream()
.filter((t) -> t.getTrader().getCity().equals("Cambridge"))
.map(Transaction::getTrader)
.sorted((t1, t2) -> t1.getName().compareTo(t2.getName()))
.distinct()
.forEach(System.out::println);
}
//4. 返回所有交易员的姓名字符串,按字母顺序排序
@Test
public void test4(){
transactions.stream()
.map((t) -> t.getTrader().getName())
.sorted()
.forEach(System.out::println);
System.out.println("-----------------------------------");
String str = transactions.stream()
.map((t) -> t.getTrader().getName())
.sorted()
.reduce("", String::concat);
System.out.println(str);
System.out.println("------------------------------------");
transactions.stream()
.map((t) -> t.getTrader().getName())
.flatMap(TestTransaction::filterCharacter)
.sorted((s1, s2) -> s1.compareToIgnoreCase(s2))
.forEach(System.out::print);
}
public static Stream<String> filterCharacter(String str){
List<String> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch.toString());
}
return list.stream();
}
//5. 有没有交易员是在米兰工作的?
@Test
public void test5(){
boolean bl = transactions.stream()
.anyMatch((t) -> t.getTrader().getCity().equals("Milan"));
System.out.println(bl);
}
//6. 打印生活在剑桥的交易员的所有交易额
@Test
public void test6(){
Optional<Integer> sum = transactions.stream()
.filter((e) -> e.getTrader().getCity().equals("Cambridge"))
.map(Transaction::getValue)
.reduce(Integer::sum);
System.out.println(sum.get());
}
//7. 所有交易中,最高的交易额是多少
@Test
public void test7(){
Optional<Integer> max = transactions.stream()
.map((t) -> t.getValue())
.max(Integer::compare);
System.out.println(max.get());
}
//8. 找到交易额最小的交易
@Test
public void test8(){
Optional<Transaction> op = transactions.stream()
.min((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()));
System.out.println(op.get());
}
}
6、并行流与串行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换。
![](https://i-blog.csdnimg.cn/blog_migrate/a9cce9ecf70e46c31408677a88914a50.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ca3aa6a5f702750b0fc52949d0033d38.png)
//采用分而治之的思想。数越大。越能体现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());//得出毫秒值
}
/**
* java8并行流
*/
@Test
public void test3(){
long start = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(0L, 10000000000L)
.parallel()
//.reduce(0,Long::sum);//从0开始累加
.sum();
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("耗费的时间为: " + (end - start));
}
}
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.of(T t):创建一个 Optional 实例,但of()里不能传null。否则报空指针
/**
* Optional.of(),创建一个实例,但of()里不能传null,否则报空指针异常
*/
@Test
public void test1(){
Optional<Employee> op = Optional.of(new Employee());
Employee emp = op.get();
System.out.println(emp);
}
Optional.empty():就是创建一个空实例。
/**
* Optional.empty()就是创建一个空实例。
*/
@Test
public void test00(){
Optional<Employee> op = Optional.empty();
System.out.println(op.get());
}
Optional.ofNullable(T t):传对象,创建有实例的Optional。传null就创建一个空实例
@Test
public void test2(){
Optional<Employee> op = Optional.ofNullable(null);
System.out.println(op.get());
}
isPresent():判断是否包含值
@Test
public void test03(){
Optional<Employee> op = Optional.ofNullable(new Employee());
if (op.isPresent()) {
Employee employee = op.get();
}
}
orElse(T t):T里有值就用T例的,没有就返回orElse的默认值
@Test
public void test3(){
Optional<Employee> op = Optional.ofNullable(new Employee());
//如果op里面有值,就取值,没有值就取“张三”这个默认值
Employee emp = op.orElse(new Employee("张三"));
System.out.println(emp);
}
orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值
@Test
public void test3(){
Optional<Employee> op = Optional.ofNullable(new Employee());
//类似于,如果上面没有值,那么就调用这个方法的新new出来的值
Employee emp2 = op.orElseGet(() -> new Employee());
System.out.println(emp2);
}
map(Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional.empty()
flatmap(Function mapper):与 map 相似,要求返回值必须是 Optional
@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、接口
//接口中具有具体的实现方法
public interface MyFun {
default String getName(){
return "libo";
}
default Integer getAge(){
return 22;
}
}
8.1、接口中的默认方法
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
选择 父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。( 父类方法有的先用父类的,,父类没有的用接口的)
接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突( 多个接口建立同一个名称的方法,那么必须是由覆盖来解决多个冲突)
![](https://i-blog.csdnimg.cn/blog_migrate/ca9e5f89b44d3bf05153e3996ba76d00.png)
9. Date / Time API
9.1线程安全问题
传统的日期格式化:(在多线程情况下,会报错)
@Test
public void test01(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return 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();
}
采用加锁方式:
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();
}
采用java8中DateTimeFormatter、LocalDate:
@Test
public void test03(){
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");//日期格式
Callable<LocalDate> task = new Callable<LocalDate>() {
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("20200517",dtf);//日期转换
}
};
//开启10个线程池
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、本地时间 / 日期
使用 LocalDate(日期)、LocalTime(时间)、LocalDateTime(日期和时间)
/**
* 此处以LocalDateTime为例,LocalDate / LocalTime 不再一一例举…
*/
@Test
public void test1(){
//获取当前时间
LocalDateTime nowtime = LocalDateTime.now();
System.out.println(nowtime);//2020-08-10T21:03:24.710
//获取指定时间
LocalDateTime zdtime = LocalDateTime.of(2016, 11, 21, 10, 10, 10);
System.out.println(zdtime);//2016-11-21T10:10:10
//当前时间加上2年
LocalDateTime ldt3 = nowtime.plusYears(20);
//当前时间加上2天
LocalDateTime plusDays = nowtime.plusDays(2);
System.out.println(ldt3);//2040-08-10T21:03:24.710
//当前时间减去2个月
LocalDateTime ldt4 = nowtime.minusMonths(2);
System.out.println(ldt4);//2020-06-10T21:03:24.710
System.out.println(nowtime.getYear());//获取当前年
System.out.println(nowtime.getMonthValue());//获取当前月
System.out.println(nowtime.getDayOfMonth());//获取当前天在这个月是第几号
System.out.println(nowtime.getHour());//获取当前小时
System.out.println(nowtime.getMinute());//获取当前分
System.out.println(nowtime.getSecond());//获取当前秒
}
方法 | 描述 | 示例 |
now() | 静态方法,根据当前时间创建对象 | LocalDate localDate = LocalDate.now(); LocalTime localTime = LocalTime.now(); LocalDateTime localDateTime = LocalDateTime.now(); |
of() | 静态方法,根据指定日期/时间创建对象 | LocalDate localDate = LocalDate.of(2016, 10, 26); LocalTime localTime = LocalTime.of(02, 22, 56); LocalDateTime localDateTime = LocalDateTime.of(2016, 10, 26, 12, 10, 55); |
plusDays, plusWeeks,plusMonths, plusYears | 向当前 LocalDate 对象添加几天、几周、几个月、几年 | |
minusDays, minusWeeks,minusMonths, minusYears | 从当前 LocalDate 对象减去几天、几周、几个月、几年 | |
plus, minus | 添加或减少一个 Duration 或 Period | |
withDayOfMonth, withDayOfYear, withMonth, withYear | 将月份天数、年份天数、月份、年 份 修 改 为 指 定 的 值 并 返 回 新 的 LocalDate 对象 | |
getDayOfMonth | 获得月份天数(1-31) | |
getDayOfYear | 获得年份天数(1-366) | |
getDayOfWeek | 获得星期几(返回一个 DayOfWeek 枚举值) | |
getMonth | 获得月份, 返回一个 Month 枚举值 | |
getMonthValue | 获得月份(1-12) | |
getYear | 获得年份 | |
until | 获得两个日期之间的 Period 对象, 或者指定 ChronoUnits 的数字 | |
isBefore, isAfter | 比较两个 LocalDate | |
isLeapYear | 判断是否是闰年 |
9.3、时间戳
//2. Instant : 时间戳。 (使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值)
@Test
public void test2(){
Instant ins = Instant.now(); //默认使用 UTC 时区,
System.out.println(ins);
OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));//默认时区,加上8小时,才是这里真正的时区
System.out.println(odt);
System.out.println(odt.getNano());//获取纳秒值
Instant ins2 = Instant.ofEpochSecond(5);//从970年1月1日 00:00:00 元年,加上5秒
System.out.println(ins2);
}
9.4、时间 / 日期差
- Duration:计算两个时间之间的间隔
- Period:计算两个日期之间的间隔
@Test
public void test3(){
Instant ins1 = Instant.now();
System.out.println("当前时间---->"+ins1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
Instant ins2 = Instant.now();
System.out.println("所耗费时间为:" + Duration.between(ins1, ins2));
System.out.println("----------------------------------");
//当前时间 2020-08-10
LocalDate ld1 = LocalDate.now();
//指定2011-1-1号
LocalDate ld2 = LocalDate.of(2011, 1, 1);
//计算相差多长时间
Period pe = Period.between(ld2, ld1);
System.out.println(pe.toTotalMonths());//返回相差一共多少个月
System.out.println(pe.getYears());//相差几年
System.out.println(pe.getMonths());//相差几个月(是比较当前月,和指定的那个1月做比较,相差9个月)
System.out.println(pe.getDays());//相差几天(比较当前天,和指定1号那天,相差9天)
//两个时间相差几天
LocalDateTime ldt1 = LocalDateTime.now();
LocalDateTime ldt2 = LocalDateTime.of(2016, 10, 26, 12, 10, 55);
long day=ChronoUnit.DAYS.between(ldt1,ldt2);
}
9.5、时间校正器
- TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
- TemporalAdjusters : 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。
/**
* TemporalAdjuster : 时间校正器
* TemporalAdjusters提供了很多实现TemporalAdjuster的方法
*/
@Test
public void test4(){
//当前时间
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
System.out.println(ldt2);
//下一个周日
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
//自定义:下一个工作日
LocalDateTime ldt5 = ldt.with((l) -> {
LocalDateTime ldt4 = (LocalDateTime) l;
DayOfWeek dow = ldt4.getDayOfWeek();
if(dow.equals(DayOfWeek.FRIDAY)){
return ldt4.plusDays(3);
}else if(dow.equals(DayOfWeek.SATURDAY)){
return ldt4.plusDays(2);
}else{
return ldt4.plusDays(1);
}
});
System.out.println(ldt5);
}
9.6、解析与格式化
- 预定义的标准格式
- 语言环境相关的格式
- 自定义的格式
@Test
public void test5(){
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
LocalDateTime ldt = LocalDateTime.now();//获取当前时间
String strDate = ldt.format(dtf);//格式化
System.out.println(strDate);
LocalDateTime newLdt = ldt.parse(strDate, dtf);
System.out.println(newLdt);
}
9.7、时区的处理
- ZonedDate
- ZonedTime
- ZonedDateTime
/**
* 获取所支持的所有时区
*/
@Test
public void test6(){
Set<String> set = ZoneId.getAvailableZoneIds();
set.forEach(System.out::println);
}
@Test
public void test7(){
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt);
//带时区的时间效果:2020-08-10T06:54:11.658-07:00[US/Pacific]
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
System.out.println(zdt);
}
10、注解
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();
}
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());
}
}
}
11、各个类型转换
DateTimeFormatter ftf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
DateTimeFormatter ftf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
ZoneId zoneId = ZoneId.systemDefault();
System.out.println("string -> long型的年月日::"+ LocalDate.parse("2018-03-11", ftf1).atStartOfDay(zoneId).toInstant().toEpochMilli());
System.out.println("String -> LocalDate: " + LocalDate.parse("2018-03-11", ftf1));
System.out.println("String -> LocalDateTime: " + LocalDateTime.parse("2020-07-15 00:00:00", ftf2));
System.out.println("long -> String型年月日:"+ftf1.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(1594794323956l), zoneId)));
System.out.println("Long -> Date: " + new Date(1520754566856L));
System.out.println("Long -> LocalDateTime: " + LocalDateTime.ofInstant(Instant.ofEpochMilli(1594794323956l), ZoneId.systemDefault()));
System.out.println("Long -> LocalDate: " + LocalDateTime.ofInstant(Instant.ofEpochMilli(1594794323956l), ZoneId.systemDefault()).toLocalDate());
System.out.println("LocalDateTime -> Long: " + Timestamp.valueOf(LocalDateTime.now()).getTime());
System.out.println("LocalDateTime -> String: " + LocalDateTime.now().format(ftf2));
System.out.println("LocalDate -> LocalDateTime: "+LocalDateTime.of(LocalDate.now(),LocalTime.parse("00:00:00")));
System.out.println("LocalDate -> Long: "+LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli());
System.out.println("LocalDate -> Date: "+Date.from(LocalDate.now().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
System.out.println("LocalDate -> String: "+LocalDate.now().format(ftf1));
System.out.println("Date -> String: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
System.out.println("Date -> LocalDateTime: " + LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()));
System.out.println("Date -> Timestamp: " + new Timestamp(new Date().getTime()));
System.out.println("Date -> Long: " + new Date().getTime());
System.out.println("Date -> Instant: " + new Date().toInstant());
System.out.println("long -> Long型的当天开始时间:"+longTodayTimeBeginMs(1594794323956l));
System.out.println("当天开始时间Long型:"+getTodayTimeBeginMs());
System.out.println("获取当天结束时间Long型:"+getTodayTimeEndMs());
}
//当天的0点时刻
public static Long getTodayTimeBeginMs() {
//获取当前日期
LocalDateTime beginTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
System.out.println("获取当天开始时间:"+beginTime);
return LocalDateTime.from(beginTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
public static Long getTodayTimeEndMs() {
//获取当前日期
LocalDate nowDate = LocalDate.now();
//设置零点
LocalDateTime beginTime = LocalDateTime.of(nowDate, LocalTime.MAX);
System.out.println("获取当天结束时间:"+beginTime);
return LocalDateTime.from(beginTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
/**
* 当天的0点时刻(Long型)
* @return
*/
public static Long longTodayTimeBeginMs(Long time) {
LocalDate longToLocalDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault()).toLocalDate();
LocalDateTime beginTime = LocalDateTime.of(longToLocalDate, LocalTime.MIN);
return LocalDateTime.from(beginTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}