JDK1.8新特性
JDK1.8新特性简介
- 速度更快 - 优化底层源码,比如HashMap、ConcurrentHashMap
- 代码更少 - 添加新的语法Lambda表达式
- 强大的Stream API
- 便于并行
- 最大化减少空指针异常 - Optional
Lambda表达式
简介
Lambda是一个匿名函数(方法), 允许把函数作为一个方法的参数 。利用Lambda表达式可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。一般都是优化匿名内部类
只要接口中只有一个方法的接口都可以使用Lambda
/lambda表达式就是想要优化这种 匿名内部类的写法 可推导 可省略 //new Thread 构造方法就确定 Runnable runnable1 = () -> { //java能推导出 现在调用一定时runnable接口 System.out.println("当前执行的线程时:" + Thread.currentThread().getName()); }; new Thread(runnable1).start(); //1.接口 2.有且仅有一个抽象方法 -----用lambda表达的格式去描述这个方法
基础语法
无参数、无返回值的抽象方法
public class Test1 {
@Test
public void test01() {
// I1 i1 = new I1() {
// @Override
// public void method() {
// System.out.println("传统使用匿名内部类的方式");
// }
// };
// i1.method();
//Lambda表达式
//()表示I1接口中的method方法
//System.out.println("采用Lambda表达式的方式")表示method方法中代码块,如果多个语句 使用{}括起来
I1 i1 = ()-> System.out.println("采用Lambda表达式的方式");
i1.method();
}
}
interface I1{
public void method();//无参数、无返回值的抽象方法
}
有参且无返回值的
public static void main(String[] args) {
//知识点:Lambda
//接口中的抽象方法是有参且无返回值的
// I2 i2 = new I2() {
// @Override
// public void method(int a, int b) {
// System.out.println(a + b);
// }
// };
// I2 i2 = (x,y) -> System.out.println(x+y);
// i2.method(10, 20);
I2 i2 = (int x,int y) -> System.out.println(x+y);
i2.method(10, 20);
}
多个参数、无返回值的抽象方法
public class Test1 {
@Test
public void test01() {
I1 i1 = (x,y,z)->
System.out.println("采用Lambda表达式的方式 " + x + y + z);
i1.method(1000,2000,3000);
}
}
interface I1{
//多个参数、无返回值的抽象方法
public void method(int num1,int num2,int num3);
}
多个参数、有返回值的抽象方法
public static void main(String[] args) {
//知识点:Lambda
//接口中的抽象方法是有参且有返回值的
// I3 I3 = new I3() {
// @Override
// public int method(int a, int b) {
// return a+b;
// }
// };
I3 i3 = (a,b) -> a+b;
int method = i3.method(10, 20);
System.out.println(method);
}
注意点
- 重写方法的形参只有一个时,可以不加小括号,
- 代码块中 只有一行时 可以不加{},如过有返回值也不用写return
- Lambda表达式当中不允许声明一个与局部变量同名的参数或者局部变量
- Lambda表达式中访问外层的局部变量,外层的局部变量自动变成隐式常量,默认添加final
- 重写方法的形参同时加类型或同时不加类型
public class Test1 {
@Test
public void test01() {
int x;
int num = 10;
I1 i1 = x -> System.out.println(x + (num++));
i1.method(1000);
I2 i2 = (int x,int y) -> {
int result = x+y;
return result;
};
int result = i2.method(10, 20);
System.out.println(result);
}
}
interface I1{
public void method(int num1);
}
interface I2{
public int method(int num1,int num2);
}
练习
- 调用Collections.sort()方法,通过定制排序比较两个Student对象(先按年龄比较,年龄相同按照薪资比较),使用Lambda表达式作为参数传递
public class Test1 {
@Test
public void test01() {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800, Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 36, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON));
Collections.sort(stuList, (stu1,stu2)-> {
if(a.getAge() == b.getAge()){
return Double.compare(a.getSalary(),b.getSalary());
}
return a.getAge()-b.getAge();
});
for (Student stu : stuList) {
System.out.println(stu);
}
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
- 创建I1接口,创建抽象方法:public String getValue(String str),在测试类中编写方法使用接口作为参数,将一个字符串转为大写,并作为方法的返回值
public class Test02 {
public static void main(String[] args) {
String strHandler=strHander("abcd",(str)->str.toUpperCase());
System.out.println("strHandler = " + strHandler);
// String strHandler2=strHander("abcd", new I1() {
// @Override
// public String getValue(String str) {
// return str.toUpperCase();
// }
// });
// System.out.println("strHandler = " + strHandler2);
}
public static String strHander(String str,I1 i1){
return i1.getValue(str);
}
}
interface I1{
String getValue(String str);
}
- 创建I1<T,R>接口,泛型T为参数,R为返回值,创建抽象方法:public R add(T t1,T t2),在测试类中编写方法使用接口作为参数,计算两个long类型的和
public class Test1 {
@Test
public void test01() {
Long addLong = addLong(100L, 200L, (x,y)-> x+y);
System.out.println(addLong);
}
public static Long addLong(Long l1,Long l2,I1<Long,Long> i1){
return i1.add(l1, l2);
}
}
interface I1<T,R>{
public R add(T t1,T t2);
}
函数式接口
简介
函数式接口是指仅仅只包含一个抽象方法的接口,jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。配合Lambda表达式一起使用
四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 | T | void | void accept(T t); |
Supplier 供给型接口 | void | T | T get(); |
Function<T, R> 函数型接口 | T | R | R apply(T t); |
Predicate 断言型接口 | T | boolean | booelan test(T t); |
BiConsumer<T, U> | T,U | void | 对类型为T,U参数应用操作。包含方法为void accept(T t,U u); |
BiFunction<T, U, R> | T,U | R | 对类型为T,U参数应用操作,并返回R类型的结果。包含方法为R apply(T t,U u); |
UnaryOperator extends Function<T, T> | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为T apply(T t); |
BinaryOperator extends BiFunction<T,T,T> | T,T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(T t1,T t2); |
ToIntFunction ToLongFunction ToDoubleFunction | T | int long double | 分别计算int、long、double值的函数 |
IntFunction LongFunction DoubleFunction | int long double | R | 参数为int、long、double类型的函数 |
public static void main(String[] args) {
//supplier生产型 无参数有返回值
Supplier<String> supplier = ()->"abc";
String s = supplier.get();
//consumer消费型,有参无返回值 用于输出等
Consumer<String> consumer1 = str-> System.out.println(str);
consumer1.accept("abc");//abc
Consumer<String> consumer2 = str-> System.out.println("第二个consumer:"+str);
//andThen方法 先执行自己的accept ,再执行andThen中的方法
consumer1.andThen(consumer2).accept("abc");//abc /n 第二个consumer:abc
//断言型,有参 返回Boolean值
//设置一个返回boolean值的语句,并设置比较目标
Predicate<Integer> predicate = i-> i>2;//new MyPre()
//用参数 1与目标值进行比较
boolean test2 = predicate.test(1);//false
// predicate.o引用predict1 的结果 ,和本方法的test结果 两者做||运算
Predicate<Integer> predicate1 = i->i%2==0;
boolean test = predicate.or(predicate1).test(3); //true
//predicate.and引用predict1 的结果 ,和本方法的test结果 两者做&&运算
//boolean test = predicate.and(predicate1).test(3); //false
System.out.println(test);
//predicate.negate() 对断言结果进行取反操作
boolean test1 = predicate.negate().test(3);
System.out.println(test1);
//判断参数与设置的目标是否相等
Predicate predicate2 = Predicate.isEqual("abc");
boolean abc = predicate2.test("abc"); //true
//功能型 R apply(T t); 输入一个 输出一个
Function<String, Integer> function = str-> Integer.valueOf(str);
System.out.println("120");
Integer apply = function.apply("100");//100
//在功能型接口中 andThen 先执行自身的apply 再将结果作为参数给引用的function1 执行apply
//return (T t) -> after.apply(apply(t));
Function<Integer,String> function1 = i->String.valueOf(i);
String apply1 = function.andThen(function1).apply("100");
//在功能型接口中 compose 引入的function 再将结果作为参数给 执行apply
//return (T t) -> after.apply(apply(t));
Integer apply2 = function.compose(function1).apply(200);
}
方法、构造方法和数组引用
方法、构造方法和数组引用就是Lamdba的另一种表现形式
方法引用
若Lamdba表达式中的内容由方法已经实现了,可以使用方法引用这个技能
当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面
对象::实例方法
Lambda表达式中调用方法的参数类型和返回值必须和函数式接口中的抽象方法一致
public class Test1 {
@Test
public void test01() {
// I1 i1 = (x)->System.out.println(x);
//
i1.method("abcd");
//println里的参数列表和返回值类型必须和method方法一致才行
PrintStream ps = System.out;
I1 i1 = ps::println;//对象::实例方法
i1.method("abcd");
}
}
interface I1{
public void method(String str);
}
类名::静态方法
Lambda表达式中调用方法的参数类型和返回值必须和函数式接口中的抽象方法一致
public class Test1 {
@Test
public void test01() {
// Comparator<Integer> com = (x,y)-> Integer.compare(x, y);
// int compare = com.compare(10, 20);
// System.out.println(compare);
//类名::静态方法
Comparator<Integer> com = Integer::compare;
int compare = com.compare(10, 20);
System.out.println(compare);
}
}
类名::实例方法
Lambda表达式参数列表中第一个参数必须是实例方法的调用者
Lambda表达式参数列表中第二个参数必须是实例方法的参数
public class Test1 {
@Test
public void test01() {
// I1<String> i1 = (x,y) -> x.equals(y);
// boolean method = i1.method("abc", "abc");
// System.out.println(method);
//类名::实例方法
//注意:Lambda表达式参数列表中第一个参数是equals方法的调用者,
// Lambda表达式参数列表中第二个参数是equals方法的参数
I1<String> i1 = String::equals;
boolean method = i1.method("abc", "abc");
System.out.println(method);
}
}
interface I1<T>{
public boolean method(T t1,T t2);
}
构造方法引用
类名::new
需要调用的构造方法的参数列表必须和函数式接口中抽象方法的参数列表一致
public class Test1 {
@Test
public void test01() {
//需求:创建学生对象
I1<Student> i1 = Student::new;
// System.out.println(i1.method());
// System.out.println(i1.method("桥本有菜",24));
System.out.println(i1.method("桥本有菜",24,8888,Course.JAVA));
}
}
interface I1<T>{
// public T method();
public T method(String name,int age,double salary,Course course);
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
数组引用
语法格式:type[]::new
public class Test1 {
@Test
public void test01() {
//创建数组
I1<String[]> i1 = String[]::new;
System.out.println(Arrays.toString(i1.method(10)));
}
}
interface I1<T>{
public T method(int capacity);
}
Stream
简介
Stream(流)是数据渠道,用于操作数据源(集合、数组等),生成元素序列。换言之,集合是存储数据的容器,流使用操作这些数据的
Stream可以对集合进行非常复杂的查找、过滤、映射数据等操作,类似于SQL执行数据库查询。Stream提供了一种高效且易于使用的处理数据的方式
注意:
- Stream不会存储数据
- Stream不会改变源数据,通过一系列操作数据源会返回一个持有结果的新Stream
- Stream操作是延迟执行的,意味着流会等到需要结果的时候才执行
执行步骤
- 创建Stream:通过数据源(集合、数组等)获取一个Stream
- 中间操作:中间操作链,对源数据的数据进行处理
- 终止操作:执行中间操作,并产生结果
创建Stream
public class Test1 {
@Test
public void test01() {
//方式一:通过Collection接口提供的stream()-串行流或parallelStream()-并行流 获取流对象
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//方式二:通过Arrays的静态方法stream()获取流对象
String[] strs = new String[10];
Stream<String> stream2 = Arrays.stream(strs);
//方式三:通过Stream的静态方法of()获取流对象
Stream<String> stream3 = Stream.of("aaa","bbb","ccc");
//方式四:创建无限流
//iterate()迭代
Stream<Integer> stream4 = Stream.iterate(1, (x)->x+=100);
stream4.limit(3).forEach(System.out::println);
//方式五:创建无限流
//generate()生成
Stream<Double> stream5 = Stream.generate(()->Math.random());
stream5.limit(3).forEach(System.out::println);
}
}
注意:多个中间操作可以连接成一个流水线,除非流水线触终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为惰性求值/延迟加载
中间操作 - 筛选与切片
方法 | 描述 |
---|---|
filter(Predicate p) | 从流中排除元素 |
limit(long maxSize) | 设置限制数据条数 |
skip(long n) | 跳过元素,返回跳过n个元素的流,若流中不满足n个元素则返回空流。与limit()互补 |
distinct() | 筛选,流通过元素对象的hashCode()和equals()方法去除重复元素 |
如果没有终止操作,中间操作就不会被调用,终止操作时一次性全部处理,这种称为惰性求值/延迟加载
public class Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//需求1:过滤掉小于5000的学生对象
Stream<Student> stream = stuList.stream().filter((x)-> {
System.out.println("中间操作");
return x.getSalary()>5000;
});
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
@Test
public void test02() {
//需求2:过滤掉小于5000的学生对象,并显示3条
//注意:因为限制了数据条数,所以满足数据条数后,后续的操作就不再运行了,效率就提高了
Stream<Student> stream = stuList.stream().filter((x)-> {
System.out.println("短路");
return x.getSalary()>5000;
}).limit(3);
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
@Test
public void test03() {
//需求3:过滤掉小于5000的学生对象,并跳过第1个学生对象
Stream<Student> stream = stuList.stream().
filter((x)-> x.getSalary()>5000).
skip(1);
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
@Test
public void test04() {
//需求4:过滤掉小于5000的学生对象,并筛选掉重复元素
//Stream底层通过元素对象(Student对象)的hashCode()和equals()方法去除重复元素
Stream<Student> stream = stuList.stream().
filter((x)-> x.getSalary()>5000).
distinct();
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
中间操作 - 映射
方法 | 描述 |
---|---|
map(Function<?, ? > mapper) | 将流中所有元素映射成一个新的元素或者提取信息 |
flatMap(Function<?, ? extends Stream<? >> mapper) | 将流中的流整合(整合成平面/平铺流) |
public class Test1 {
List<String> nameList = Arrays.asList("张三","李四","王五","赵六","孙七","吴八");
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//map() - 将流中所有元素映射成一个新的元素 或者 提取信息
//方式1:映射成一个新的元素
//需求:将流中所有字符串拼接成新的字符串
nameList.stream().map((str)-> str.charAt(0)).forEach(System.out::println);
//方式2:映射成提取信息
//需求:把原来流中的学生对象的姓名挨个输出
stuList.stream().map((stu)-> stu.getName()).forEach(System.out::println);
@Test
public void test02 () {//把原来流中的学生对象的姓名拼接成新的字符串
List<String> s = stuList.stream().map((stu) -> stu.getName()).collect(Collectors.toList());
System.out.println("s = " + s);
}
}
@Test
public void test02() {
//带着需求学flatMap()
//flatMap() - 将流中的流整合(整合成平面/平铺流)
//需求:将nameList里的字符串转换为字符输出
//解决方案1:使用map()完成需求,可以看到流里包含另外的流,非常麻烦
Stream<Stream<Character>> stream = nameList.stream().
map(Test1::getCharacterStream);//{{'张','三'},{'李','四'},...}
stream.forEach((sm) -> {
sm.forEach(System.out::println);
});
//解决方案2:使用flatMap()完成需求,将流中的流一并整合
nameList.stream().flatMap((str)-> getCharacterStream(str)).
forEach(System.out::println);//{'张','三'},{'李','四'},...
}
//将字符串拆分出字符转换为流的方法
public static Stream<Character> getCharacterStream(String str){//"张三"
ArrayList<Character> list = new ArrayList<>();//'张','三'
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
中间操作 - 排序
方法 | 解释 |
---|---|
sorted() | 使用元素原有排序规则 - Comparable |
sorted(Comparator<? super T> comparator) | 使用自定义排序规则 - Comparator |
public class Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON));
@Test
public void test01() {
//使用元素原有排序规则(Comparable<T>)
//需求:按照年龄排序
stuList.stream().sorted().forEach(System.out::println);
}
@Test
public void test02() {
//使用自定义排序规则(Comparator<T>)
//需求:按照工资排序
stuList.stream().sorted((e1,e2)->{
if(e1.getSalary() == e2.getSalary()){
return 1;
}
return (int)(e1.getSalary() - e2.getSalary());
}).forEach(System.out::println);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
...
}
终止操作 - 匹配与查找
方法 | 描述 |
---|---|
allMatch(Predicate<? super T> predicate) | 检查是否匹配所有元素 |
anyMatch(Predicate<? super T> predicate) | 检查是否匹配至少一个元素 |
noneMatch(Predicate<? super T> predicate) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回任意一个元素(但效果不好) |
count() | 返回流中元素的个数 |
max(Comparator<? super T> comparator) | 返回流中最大值 |
min(Comparator<? super T> comparator) | 返回流中最小值 |
public class Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON));
@Test
public void test01() {
//需求1:检查流中所有元素是否匹配 工资>5000
boolean allMatch = stuList.stream().allMatch((stu) -> stu.getSalary()>5000);
System.out.println(allMatch);//false
//需求2:检查流中所有元素至少匹配一个 工资>5000
boolean anyMatch = stuList.stream().anyMatch((stu) -> stu.getSalary()>5000);
System.out.println(anyMatch);//true
//需求3:检查流中所有元素是否没有匹配 工资>5000
boolean noneMatch = stuList.stream().noneMatch((stu) ->
stu.getSalary()>5000);
System.out.println(noneMatch);
//需求3:返回工资最高的学生信息
Optional<Student> findFirst = stuList.stream().
sorted((stu1,stu2)->Double.compare(
stu1.getSalary(),stu2.getSalary())).
findFirst();
Student stu = findFirst.get();
//这种写法防止NullPointerException出现
//Student stu = findFirst.orElse(new Student());
System.out.println(stu);
//需求4:返回随机学生信息(但效果不好)
Optional<Student> findAny = stuList.stream().findAny();
Student stu = findAny.get();
System.out.println(stu);
//需求5:获取学生个数
long count = stuList.stream().count();
System.out.println(count);
//需求6:获取最高工资的学生信息
Optional<Student> max = stuList.stream().
max((stu1,stu2)->Double.compare(stu1.getSalary(),stu2.getSalary()));
System.out.println(max.get());
//需求7:获取最低工资的学生信息
Optional<Student> min = stuList.stream().
min((stu1,stu2)->Double.compare(stu1.getSalary(),stu2.getSalary()));
System.out.println(min.get());
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
终止操作 - 归约
归约:将流中的元素反复结合起来,得到一个值
map+reduce的连接通常称为map_reduce模式,因Google用它进行网络搜索而出名
方法 | 描述 |
---|---|
reduce( T identity , BinaryOperator accumulator) | 参数:(初始值,结合逻辑) |
reduce(BinaryOperator accumulator) | 参数:(结合逻辑) |
public class Test1 {
List<Integer> numList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//需求:获取numList集合中元素的总和
Integer reduce = numList.stream().
reduce(0, (x,y)->x+y);
System.out.println(reduce);
}
@Test
public void test02() {
//需求:获取stuList集合中所有学生工资总和
Optional<Double> reduce = stuList.stream().
map(Student::getSalary).reduce(Double::sum);
Double sumSalary = reduce.get();
System.out.println(sumSalary);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;
终止操作 - 收集
收集:将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
方法 | 描述 |
---|---|
collect(Collector<? super T, A, R> collector) | 把元素放入Collector集合中 |
public class Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//把数据收集到集合中
//需求1:把当前学生姓名提取出来,并把数据放入List集合中
List<String> list = stuList.stream().
map(Student::getName).
collect(Collectors.toList());
list.forEach(System.out::println);
//需求2:把当前学生姓名提取出来,并把数据放入Set集合中
Set<String> set = stuList.stream().
map(Student::getName).collect(Collectors.toSet());
set.forEach(System.out::println);
//需求3:把当前学生姓名提取出来,并把数据放入指定集合中
HashSet<String> hashSet = stuList.stream().
map(Student::getName).collect(Collectors.toCollection(HashSet::new));
hashSet.forEach(System.out::println);
}
@Test
public void test02() {
//收集流中的各种数据
//需求1:收集/获取学生个数
Long count = stuList.stream().
map(Student::getName).collect(Collectors.counting());
System.out.println(count);
//需求2:收集/获取学生平均工资
Double avg = stuList.stream().
collect(Collectors.averagingDouble(Student::getSalary));
System.out.println(avg);
//需求3:收集/获取学生总工资
Double sum = stuList.stream().
collect(Collectors.summingDouble(Student::getSalary));
System.out.println(sum);
//需求4:收集/获取学生工资最大值
Optional<Double> max = stuList.stream().map(Student::getSalary).
collect(Collectors.maxBy(Double::compareTo));
System.out.println(max.get());
//需求5:收集/获取学生工资最小值
Optional<Double> min = stuList.stream().map(Student::getSalary).
collect(Collectors.minBy(Double::compareTo));
System.out.println(min.get());
//需求6:收集/获取工资最多的学生信息
Optional<Student> maxStu = stuList.stream().
collect(Collectors.maxBy(
(stu1,stu2)-> (int)(stu1.getSalary()-stu2.getSalary())));
System.out.println(maxStu.get());
//需求7:收集/获取工资最少的学生信息
Optional<Student> minStu = stuList.stream().
collect(Collectors.minBy(
(stu1,stu2)-> (int)(stu1.getSalary()-stu2.getSalary())));
System.out.println(minStu.get());
}
@Test
public void test03() {//分组
//需求:按照学科分组
Map<Course, List<Student>> map = stuList.stream().collect(
Collectors.groupingBy(Student::getCourse));
System.out.println(map);
}
@Test
public void test04() {//多级分组
//需求:按照学科分组,在按照年龄分组
Map<Course, Map<String, List<Student>>> map = stuList.stream().
collect(Collectors.groupingBy(
Student::getCourse,Collectors.groupingBy((stu)->{
if(((Student)stu).getAge() < 28){
return "青年";
}else if(((Student)stu).getAge() < 40){
return "中年";
}else{
return "老年";
}
})));
System.out.println(map);
}
@Test
public void test05() {//分区
//需求:按照工资5000为标准分区
Map<Boolean, List<Student>> map = stuList.stream().collect(
Collectors.partitioningBy((stu) -> stu.getSalary()>5000));
System.out.println(map);
}
@Test
public void test06() {//获取元素中字段的各种信息
//需求:获取学生工资信息,再获取总值、平均值、最大值、最小值
DoubleSummaryStatistics collect = stuList.stream().collect(
Collectors.summarizingDouble(Student::getSalary));
System.out.println(collect.getSum());
System.out.println(collect.getAverage());
System.out.println(collect.getMax());
System.out.println(collect.getMin());
}
@Test
public void test07() {//拼接信息
//需求:拼接学生姓名
String str1 = stuList.stream().map(Student::getName).collect(
Collectors.joining());
System.out.println(str1);
String str2 = stuList.stream().map(Student::getName).collect(
Collectors.joining(","));
System.out.println(str2);
String str3 = stuList.stream().map(Student::getName).collect(
Collectors.joining(",","--","--"));
System.out.println(str3);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
并行流与串行流
并行流就是把一个内容拆分成多个数据块,并用不同的线程分别处理每个数据块的流。Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API可以声明性地通过 parallel() - 并行流 与sequential()-顺序流 之间进行切换。
注意
- 默认为顺序流/串行流
- 并行流一般在大数据搜索里使用到
- JDK1.8之前也有并行流,叫做Fork/Join并行计算框架,单独文件夹抽时间学习
public class Test1 {
@Test
public void test01() {
//需求:使用并行流计算1-10000000L之和
OptionalLong reduce = LongStream.
range(0, 10000000L).//生成1-10000000的数流
parallel(). //转换为并行流
reduce(Long::sum); //求和
System.out.println(reduce);
}
}
Optional Day26
Optional类(java. util. Optional)是一个容器类,代表一个存在或不存在的值,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常
此类的设计就是更好的避免空指针异常
方法 | 描述 |
---|---|
Optional.of(T t) | 创建一个Optional实例,不能为空 |
Optional.empty() | 创建一 个空的 Optional实例 |
Optional.ofNullable(T t) | 若t不为null,创建Optional实例,否则创建空实例 |
get() | 获取Optional实例里的对象 |
isPresent() | 判断是否包含值 |
orElse(T t) | 如果调用对象包含值, 返回该值,否则返回t |
orElseGet(Supplier s) | 如果调用对象包含值,返回该值,否则返回s获取的值 |
map(Function f) | 如果有值对其处理,并返回处理后的Optional,否则返回optional. empty() |
flatMap(Function mapper) | 与map 类似,要求返回值必须是Optional |
public class Test1 {
@Test
public void test01() {
//创建一个Optional实例,把对象封装到Optional容器里
Optional<Student> op = Optional.of( new Student("aaa", 26, 6666, Course.HTML));
//创建一个空的Optional容器
Optional<Student> op = Optional.empty();
//创建一个Optional实例,若对象为null->创建空实例,若对象为不为null->创建Optional实例
Optional<Student> op = Optional.ofNullable(
new Student("bbb", 26, 7777, Course.PYTHON));
//判断容器里是否包含对象
if(op.isPresent()){//包含
System.out.println("获取容器里的对象:" + op.get().getName());
}else{//不包含
System.out.println("容器里的对象为null");
}
//如果容器里有对象就返回,否则就返回新对象
Student stu = op.orElse(new Student("ccc", 26, 8888, Course.JAVA));
System.out.println(stu.getName());
//不同情况下可以返回不同对象,orElseGet()比orElse()可变性更强,可以做其他功能
boolean bool = true;
stu = op.orElseGet(()->{
if(bool){
return new Student("吴彦祖", 26, 8888, Course.JAVA);
}else{
return new Student("麻生希", 26, 8888, Course.JAVA);
}
});
//获取原容器中的某个值并返回新容器中
//map(Function<? super T, ? extends U> mapper)
Optional<String> map = op.map(Student::getName);
System.out.println(map.get());
//与map 类似,要求返回值必须是Optional
//flatMap(Function<? super T, Optional<U>> mapper)
Optional<String> flatMap = op.flatMap((e)->Optional.of(e.getName()));
System.out.println(flatMap.get());
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
接口的默认方法与静态方法
从JDK1.8开始,接口中可以有默认方法,既default修饰的方法,此方法可以让接口的实现类所调用,而接
接口中的静态方法直接用接口名调用即可
public class Test1 {
@Test
public void test01() {
MyClass myClass = new MyClass();
//default默认修饰使用接口实现类的事例调用
myClass.defaultMethod();
//static方法直接接口名调用
I1.staticMethod();
}
}
interface I1{
default void defaultMethod(){
System.out.println("接口中的默认方法");
}
public static void staticMethod(){
System.out.println("接口中的静态方法");
}
}
class MyClass implements I1{}
接口默认方法的”类优先”原则及接口冲突:
如果一个接口中定义了一个默认方法,而接口实现类的父类定义了一个同名的方法时,选择父类中的方法
接口冲突:如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须重写覆盖该方法来解决冲突
public class Test1 {
@Test
public void test01() {
MyClass myClass = new MyClass();
myClass.method(); //System.out.println("Father类中的默认方法");
}
}
interface I1{
default void method(){
System.out.println("I1接口中的默认方法");
}
}
class Father {
public void method(){
System.out.println("Father类中的默认方法");
}
}
class MyClass extends Father implements I1 {}
public class Test1 {
@Test
public void test01() {
MyClass myClass = new MyClass();
myClass.method();
}
}
interface I1{
default void method(){
System.out.println("I1接口中的默认方法");
}
}
interface I2{
default void method(){
System.out.println("I2接口中的默认方法");
}
}
class MyClass implements I1,I2 {
@Override
public void method() {
//I1.super.method();
I2.super.method();
}
}
日期组件
JDK1.8提供的新日期类都是不可变的,既不管怎么样的改变,都会产生一个新的实例,他们都是线程安全的
日期组件遵循与IOS-8601世界时间标准
组件简介
包路径 | 类名 | 描述 |
---|---|---|
java.time | 针对日期和时间操作的包 | |
LocalDate | 用于表示日期的类 | |
LocalTime | 用于表示时间的类 | |
LocalDateTime | 用于表示日期时间的类 | |
Instant | 时间戳类(1970.1.1 0:0:0 到现在的毫秒数) | |
Period | 两个日期间隔类 | |
Duration | 两个时间间隔类 | |
java.time.chrono | 针对日期时间特殊格式操作的包 | |
JapaneseChronology | 日本帝国历法系统类 | |
ThaiBuddhistChronology | 泰国佛教日历系统类 | |
java.time.format | 针对时间日期时间格式化操作的包 | |
DateTimeFormatter | 格式化日期时间类 | |
java.time.temporal | 针对时间矫正操作的包 | |
java.time.zone | 针对时区操作的包 |
日期时间类、时间戳、间隔类
public class Test1 {
@Test
public void test01() {
//LocalDate LocalTime LocalDateTime
//这三个日期类的使用大致一样
//获取当前日期时间对象
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println(ldt1);
//获取指定日期时间对象
LocalDateTime ldt2 = LocalDateTime.of(2020, 1, 23, 8, 30, 10, 10);
System.out.println(ldt2);
//获取ldt1推后的时间日期对象
LocalDateTime ldt3 = ldt1.plusYears(2);
System.out.println(ldt3);
//获取ldt1提前的时间日期对象
LocalDateTime ldt4 = ldt3.minusMonths(2);
System.out.println(ldt4.getYear());
System.out.println(ldt4.getMonthValue());
System.out.println(ldt4.getDayOfMonth());
System.out.println(ldt4.getHour());
System.out.println(ldt4.getMinute());
System.out.println(ldt4.getSecond());
}
@Test
public void test02() {
//使用时间戳(从1970年1月1日0:0:0到现在的毫秒值)
//默认创建UTC(世界标准时间)时区的时间戳对象
Instant now1 = Instant.now();
System.out.println(now1);
//获取偏移8小时的偏移日期时间对象
OffsetDateTime odt = now1.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);
//获取时间戳的毫秒值形式
System.out.println(now1.toEpochMilli());
//获取一个1970年1月1日0:0:0 往后退1秒的时间戳对象
Instant now2 = Instant.ofEpochSecond(1);
System.out.println(now2);
}
@Test
public void test03() throws InterruptedException {
//Duration:时间间隔类
Instant now1 = Instant.now();
Thread.sleep(1000);
Instant now2 = Instant.now();
//获取时间间隔类对象
Duration duration1 = Duration.between(now1, now2);
System.out.println(duration1.toMillis());
System.out.println("-----------------------------");
LocalTime lt1 = LocalTime.now();
Thread.sleep(1000);
LocalTime lt2 = LocalTime.now();
//获取时间间隔类对象
Duration duration2 = Duration.between(lt1, lt2);
System.out.println(duration2.toMillis());
}
@Test
public void test04() throws InterruptedException {
//Period:日期间隔类
LocalDate ld1 = LocalDate.now();
Thread.sleep(1000);
LocalDate ld2 = LocalDate.of(2020, 12, 31);
Period period = Period.between(ld1, ld2);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());
}
}
日期时间格式化类-DateTimeFormatter
public class Test1 {
@Test
public void test01() {
//格式化日期时间类
LocalDateTime ldt1 = LocalDateTime.now();
//获取本地标准的日期时间格式化对象
DateTimeFormatter dtf1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String strDateTime1 = ldt1.format(dtf1);//格式化时间日期
System.out.println(strDateTime1);
//自定义日期时间格式化对象
DateTimeFormatter dtf2 = DateTimeFormatter.
ofPattern("yyyy年MM月dd日 HH:mm:ss");
String strDateTime2 = ldt1.format(dtf2);//格式化时间日期
System.out.println(strDateTime2);
//将指定格式的字符串解析成LocalDateTime对象
LocalDateTime parse = LocalDateTime.parse("2020年03月12日 11:04:14", dtf2);
System.out.println(parse);
}
}
时间矫正器类-TemporalAdjuster
public class Test1 {
@Test
public void test01() {
//时间矫正器
LocalDateTime ldt1 = LocalDateTime.now();
//设置指定月份
LocalDateTime ldt2 = ldt1.withMonth(10);
System.out.println(ldt2);
//设置下一个周末
LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
//自定义时间矫正器:设置下一个工作
LocalDateTime ldt4 = ldt1.with((temporal)->{
LocalDateTime ldt = (LocalDateTime) temporal;
DayOfWeek week = ldt.getDayOfWeek();
if(week.equals(DayOfWeek.FRIDAY)){//周五
return ldt.plusDays(3);
}else if(week.equals(DayOfWeek.SATURDAY)){//周六
return ldt.plusDays(2);
}else{
return ldt.plusDays(1);
}
});
System.out.println(ldt4);
}
}
时区类
public class Test1 {
@Test
public void test01() {
//时区类
//获取所有时区字符串
Set<String> set = ZoneId.getAvailableZoneIds();
for (String str : set) {
System.out.println(str);
}
//获取指定时区的日期时间对象
LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(ldt1);
//获取指定时区的日期时间对象 + 偏移量
LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime zonedDateTime = ldt2.atZone(ZoneId.of("Asia/Tokyo"));
System.out.println(zonedDateTime);
}
}
重复注解及类型注解
jdk1.8开始可以重复注解
ps:一个类可有多个同样的注解
package duplicate_annotation;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import static java.lang.annotation.ElementType.*;
//重复注解
@Author(name="hahah")
@Author(name="dd")
public class Test01 {
public static void main(String[] args) throws NoSuchMethodException {
//需求1:获取Test1的多个作者注解
Class<Test01> c1 = Test01.class;
Author[] as = c1.getAnnotationsByType(Author.class);
for (Author a : as) {
System.out.println(a.name());
}
//获取method方法中的注解
//方法1
Class<Myclass> c2 = Myclass.class; //获取Myclass的Class对象
Method method = c2.getMethod("method", String.class);//获取方法对象
Annotation[][] p = method.getParameterAnnotations();//获取参数中注解列表(有可能一个参数多个注解)
for (Annotation[] annotations : p) {//遍历数组
for (Annotation annotation : annotations) {//获取具体注解
System.out.println("annotation = " + annotation);//annotation = @duplicate_annotation.Author(name=波多)
}
}
//方法2
Parameter[] parameters = method.getParameters();//获取方法参数对象
for (Parameter parameter : parameters) {//遍历
Author annotation = parameter.getAnnotation(Author.class);//获取参数对象上的注解
System.out.println("annotation = "+annotation.name());//@duplicate_annotation.Author(name=波多)
}
}
}
class Myclass {
public void method(@Author(name = "波多") String str) {
}
}
//作者注解的容器
@Target(TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Authors {
//注解的数组
Author[] value();
}
//作者注解
@Repeatable(Authors.class)//相当于存储注解的容器
//TYPE_PARAMETER-类型注解:作用在参数
@Target({TYPE, PARAMETER, TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@interface Author {
String name();
}