哈喽!大家好,我是小简。今天开始学习《Java8-新特性》,此系列是我做的一个 “Java 从 0 到 1 ” 实验,给自己一年左右时间,按照我自己总结的 Java-学习路线,从 0 开始学 Java 知识,并不定期更新所学笔记,期待一年后的蜕变吧!<有同样想法的小伙伴,可以联系我一起交流学习哦!>
读作λ
表达式,它其实就是我们接口匿名实现的简化,Lambda 是一个匿名函数,Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)
Lambda 表达式的基础语法:Java8 中引入了一个新的操作符 “->
“ 该操作符称为箭头操作符或 Lambda 操作符
箭头操作符将 Lambda 表达式拆分成两部分:
语法格式一:无参数,无返回值
() -> System.out.println("Hello Lambda!");
语法格式二:有一个参数,并且无返回值
(String x) -> System.out.println(x)
语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
(x) -> System.out.println(x)
语法格式四:若只有一个参数,小括号可以省略不写
x -> System.out.println(x)
语法格式五:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
Comparator<Integer> com = (x, y) -> {
System.out.println("函数式接口");
return Integer.compare(x, y);
};
语法格式六:若 Lambda 体中只有一条语句, return 和大括号都可以省略不写
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
从匿名类到 Lambda 的转换
@Test
public void test(){
//匿名内部类
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!");
}
};
r1.run();
//Lambda 表达式
Runnable r2 = () -> System.out.println("He1lo Lambda!");
r2.run();
}
参数传递
@Test
public void test2(){
//原来使用匿名内部类作为参数传递
TreeSet<String> ts = new TreeSet<>(new Comparator<String>(){
@Override
public int compare(String x, String y) {
return Integer.compare(x.length(), y.length());
}
});
// Lambda表达式作为参数传递
Comparator<String> com = (x, y) -> Integer.compare(x.length(), y.length());
TreeSet<String> ts2 = new TreeSet<>(com);
}
自定义一个函数式接口
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}
使用 Lambda 表达式来表示该接口的一个实现(JAVA 8 之前一般是用匿名类实现的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
Java内置四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer<T> 消费型接口 | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) |
Supplier<T> 供给型接口 | 无 | T | 返回类型为T的对象,包含方法:T get(); |
Function<T,R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t); |
Predicate<T> 断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t); |
其他函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
BiFunction<T,U,R> | T, U | R | 对类型为T,U参数应用操作,返回R类型的结果。包含方法为:Rapply(T t,U u); |
UnaryOperator<T> (Function子接口) | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为:T apply(T t); |
BinaryOperator<T>(BiFunction子接口) | T, T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为:T apply(T t1,T t2); |
BiConsumer<T,U> | T, U | void | 对类型为T,U参数应用操作。 包含方法为:void accept(T t,U u) |
BiPredicate<T,U> | T,U | boolean | 包含方法为:boolean test(T t,U u) |
ToIntFunction<T> ToLongFunction<T> ToDoubleFunction<T> | T | int long double | 分别计算int、long、double值的函数 |
IntFunction<R> LongFunction<R> DoubleFunction<R> | int long double | R | 参数分别为int、long、double类型的函数 |
::
”将方法名和对象或类的名字分隔开来。
如下三种主要使用情况:
Consumer<String> con = (x) -> System.out.println(x);
//等同于
Consumer<String> con = System.out::println;
//对象的引用::实例方法名
Employee emp = new Employee(101, "张三", 18, 9999.99);
Supplier<String> sup = () -> emp.getName();
System.out.println(sup.get());
//等同于
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
//类名 :: 静态方法名
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
//等同于:
Comparator<Integer> com = Integer::compare;
//类名 :: 实例方法名
BiPredicate<String, String> bp = (x,y) -> x.equals(y);
//等同于:
BiPredicate< String,String> bp = String::equals;
//注意:当函数式接口方法的第-一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时: ClassName::methodName
Function<Integer, MyClass> fun = (n) -> new MyClass(n);
//等同于:
Function<Integer, MyClass> fun = MyClass::new;
Function<Integer, Integer[]> fun = (n) -> newInteger[n];
//等同于
Function<Integer, Integer[]> fun = Integer[]::new;
Function<Integer, String[]> fun = (args) -> new String[args];
String[] strs = fun.apply(10);
System.out.println(strs.length);
//等同于
Function<Integer, Employee[]> fun2 = Employee[]::new;
Employee[] emps = fun2.apply(20);
System.out.println(emps.length);
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,而Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过 CPU 实现计算。
在没有 Stream 之前,我们想提取出所有年龄大于18的学生,我们需要这样做:
List<Student> result=new ArrayList<>();
for(Student student:students){
if(student.getAge()>18){
result.add(student);
}
}
return result;
使用Stream,我们可以参照上面的流程示意图来做,首先产生 Stream,然后 filter 过滤,最后归并到容器中。
return students.stream().filter(s->s.getAge()>18).collect(Collectors.toList());
在创建 Stream 前,先创建一个 Employee.java,供下面测试使用。
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee() {
}
public Employee(String name) {
this.name = name;
}
public Employee(Integer id) {
this.id = id;
}
public Employee(Integer id,Integer age) {
this.id = id;
this.age = age;
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String show() {
return "测试方法引用!";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(salary);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (age != other.age)
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
return false;
return true;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
}
}
创建一个测试类 StreamAPI.java,并创建一个 test 方法。所有创建 Stream 的方式都在这个类的 test 方法中。
public class StreamaAPI {
@Test
public void test() {
}
}
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法
Collection.stream()
Collection.parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();//获取一个顺序流
Stream<String> stream2 = list.parallelStream();//获取一个并行流
Java8 中的 Arrays 的静态方法stream() 可以获取数组流:
Arrays.stream(T array)
Employee[] emps = new Employee[10];
Stream<Employee> stream3 = Arrays.stream(emps);
重载形式,能够处理对应基本类型的数组:
可以调用Stream类静态方法of(), 通过显示值创建一个流。它可以接收任意数量的参数。
Stream.of()
Stream<String> stream4 = Stream.of("aa", "bb", "cc");
stream4.forEach(System.out::println);
可以使用静态方法 Stream.iterate() 和Stream.generate(),创建无限流。
Stream.iterate()
Stream<Integer> stream5 = Stream.iterate(0, (x) -> x + 2);
stream5.limit(5).forEach(System.out::println);
Stream.generate()
Stream<Double> stream6 = Stream.generate(() -> Math.random());
stream6.limit(5).forEach(System.out::println);
一个流可以后面跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后会返回一个新的流,交给下一个操作使用。
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”
在中间操作前,先在 StreamAPI.java 中创建一个 Employee 对象的集合,供下面测试使用。
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66),
new Employee(101, "张三", 18, 9999.99),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(104, "赵六", 8, 7777.77),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
方法 | 描述 |
---|---|
filter(Predicate p) | 接收Lambda ,从流中排除某些元素 |
distinct() | 筛选,通过流所生成元素的hashCode()和equals()去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n) 互补 |
@Test
public void testFilter() {
//这里加入了终止操作 ,不然中间操作不会执行
emps.stream().filter((e) -> e.getAge() > 18)//年龄大于18岁
.forEach(System.out::println);//终止操作
}
/**
Employee [id=102, name=李四, age=59, salary=6666.66]
Employee [id=103, name=王五, age=28, salary=3333.33]
Employee [id=105, name=田七, age=38, salary=5555.55]
**/
@Test
public void testDistinct() {
emps.stream().distinct()//去除重复的元素,需要重写hashCode跟equals方法
.forEach(System.out::println);//终止操作
}
/**
Employee [id=102, name=李四, age=59, salary=6666.66]
Employee [id=101, name=张三, age=18, salary=9999.99]
Employee [id=103, name=王五, age=28, salary=3333.33]
Employee [id=104, name=赵六, age=8, salary=7777.77]
Employee [id=105, name=田七, age=38, salary=5555.55]
**/
@Test
public void testLimit() {
emps.stream().filter((e) -> e.getAge()> 18)
.limit(2)
.forEach(System.out::println);//终止操作
}
/**
Employee [id=102, name=李四, age=59, salary=6666.66]
Employee [id=103, name=王五, age=28, salary=3333.33]
**/
@Test
public void testSkip() {
emps.stream().filter((e) -> e.getAge() > 18)
.skip(2)//这里可以查找filter过滤后的数据,前两个不要,要后面的,与limit相反
.forEach(System.out::println);//终止操作
}
/**
Employee [id=105, name=田七, age=38, salary=5555.55]
**/
map
操作能够将流中的每一个元素映射为另外的元素。
方法 | 描述 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream。 |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream。 |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream。 |
@Test
public void testMapAndflatMap() {
List<String> list=Arrays.asList("aaa","bbb");
list.stream()
.map((str)->str.toUpperCase())//里面是Function
.forEach(System.out::println);
System.out.println("======================================");
//流中流
Stream<Stream<Character>> stream = list.stream()
.map(StreamAPI::filterCharacter);
//map是一个个流(这个流中有元素)加入流中 {{a,a,a},{b,b,b}}
stream.forEach(sm->{
sm.forEach(System.out::println);
});
System.out.println("=============引进flatMap=============");
//只有一个流
Stream<Character> flatMap = list.stream()
.flatMap(StreamAPI::filterCharacter);
//flatMap是将一个个流中的元素加入流中 {a,a,a,b,b,b}
flatMap.forEach(System.out::println);
}
public static Stream<Character> filterCharacter(String str){
List<Character> list=new ArrayList<>();
for (Character character : str.toCharArray()) {
list.add(character);
}
return list.stream();
}
map 跟 flatMap的区别
方法 | 描述 |
---|---|
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
@Test
public void testSorted() {
List<String> list=Arrays.asList("ccc","aaa","bbb","ddd","eee");
list.stream().sorted()
.forEach(System.out::println);
System.out.println("=======定制排序=========");
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);
}
终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void ,流进行了终止操作后,不能再次使用。
新建了一个Employee2.java,供下面测试使用。
public class Employee2 {
private int id;
private String name;
private int age;
private double salary;
private Status status;
public Employee2() {
}
public Employee2(String name) {
this.name = name;
}
public Employee2(String name, int age) {
this.name = name;
this.age = age;
}
public Employee2(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
public Employee2(int id, String name, int age, double salary, Status status) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
this.status = status;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String show() {
return "测试方法引用!";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(salary);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee2 other = (Employee2) obj;
if (age != other.age)
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
return false;
return true;
}
@Override
public String toString() {
return "Employee2 [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + ", status=" + status
+ "]";
}
public enum Status {
FREE, BUSY, VOCATION;
}
}
创建一个测试类 TestShutdown.java,test 方法写在里面
public class TestShutdown {
List<Employee2> emps = Arrays.asList(
new Employee2(102, "李四", 59, 6666.66, Employee2.Status.BUSY),
new Employee2(101, "张三", 18, 9999.99, Employee2.Status.FREE),
new Employee2(103, "王五", 28, 3333.33, Employee2.Status.VOCATION),
new Employee2(104, "赵六", 8, 7777.77, Employee2.Status.BUSY),
new Employee2(104, "赵六", 8, 7777.77, Employee2.Status.FREE),
new Employee2(104, "赵六", 8, 7777.77, Employee2.Status.FREE),
new Employee2(105, "田七", 38, 5555.55, Employee2.Status.BUSY)
);
@Test
public void test() {
}
}
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
@Test
public void testMatch(){
System.out.println("==========allMatch==============");
boolean allMatch = emps.stream().allMatch((e) -> e.getAge() > 5);
System.out.println(allMatch);
System.out.println("==========anyMatch==============");
boolean anyMatch = emps.stream().anyMatch((e)->e.getName().equals("张三"));
System.out.println(anyMatch);
System.out.println("==========noneMatch=============");
boolean noneMatch = emps.stream().noneMatch((e) -> e.getAge() < 2);//检查是否没有一个小于2
System.out.println(noneMatch);
}
方法 | 描述 |
---|---|
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用Collection接口需要用户去做迭代,称为外部迭代) |
@Test
public void testSearch(){
System.out.println("==========findFirst==============");
Optional<Employee2> findFirst = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))//按照工资排序并输出第一个
.findFirst();
System.out.println(findFirst);
System.out.println("==========findAny==============");
Optional<Employee2> findAny = emps.stream()
.filter((e)->e.getStatus().equals(Employee2.Status.BUSY))
.findAny();
System.out.println(findAny);
System.out.println("==========count==============");
long count = emps.stream().count();
System.out.println(count);
System.out.println("==========max==============");
Optional<Double> max = emps.stream()
.map(Employee2::getSalary)
.max(Double::compare);
System.out.println(max);
System.out.println("==========min==============");
Optional<Employee2> min = emps.stream()
.min((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(min);
System.out.println("==========forEach==============");
emps.stream().forEach(System.out::println);
}
方法 | 描述 |
---|---|
reduce(T identity, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回Optional<T> |
备注:map 和reduce 的连接通常称为map-reduce 模式,因Google 用它来进行网络搜索而出名。
@Test
public void testReduce() {
List<Integer> list= Arrays.asList(1,2,3);
Integer sum = list.stream().reduce(4,(x,y)->x+y);
System.out.println(sum);
Optional<Integer> sum2 = list.stream().reduce((x, y) -> x+y);
System.out.println(sum2.get());
}
/**
10
6
**/
方法 | 描述 |
---|---|
collect(Collector c) | 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法 |
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)。
另外,Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
@Test
public void testCollect1() {
System.out.println("==========toList==============");
List<String> collect = emps.stream()
.map(Employee2::getName)
.collect(Collectors.toList());
collect.forEach(System.out::println);
System.out.println("==========toSet==============");
Set<String> collect2 = emps.stream()
.map(Employee2::getName)
.collect(Collectors.toSet());//set不允许重复
collect2.forEach(System.out::println);
System.out.println("==========toCollection==============");
HashSet<String> collect3 = emps.stream()
.map(Employee2::getName)
.collect(Collectors.toCollection(HashSet::new));
collect3.forEach(System.out::println);
}
/**
==========toList==============
李四
张三
王五
赵六
赵六
赵六
田七
==========toSet==============
李四
张三
王五
赵六
田七
==========toCollection==============
李四
张三
王五
赵六
田七
**/
@Test
public void testCollect2() {
System.out.println("==========counting==============");
Long collect7 = emps.stream()
.collect(Collectors.counting());
System.out.println(collect7);
System.out.println("==========summingDouble==============");
Double collect5 = emps.stream()
.collect(Collectors.summingDouble(Employee2::getSalary));
System.out.println(collect5);
System.out.println("==========averagingDouble==============");
Double collect6 = emps.stream()
.collect(Collectors.averagingDouble((e)->e.getSalary()));
System.out.println(collect6);
System.out.println("==========summarizingDouble==============");
DoubleSummaryStatistics collect9 = emps.stream()
.collect(Collectors.summarizingDouble(Employee2::getSalary));
long count = collect9.getCount();
double average = collect9.getAverage();
double max = collect9.getMax();
double min = collect9.getMin();
double sum = collect9.getSum();
System.out.println("count:"+count);
System.out.println("average:"+average);
System.out.println("max:"+max);
System.out.println("min:"+min);
System.out.println("sum:"+sum);
}
/**
==========counting==============
7
==========summingDouble==============
48888.840000000004
==========averagingDouble==============
6984.120000000001
==========summarizingDouble==============
count:7
average:6984.120000000001
max:9999.99
min:3333.33
sum:48888.840000000004
**/
@Test
public void testCollect3() {
//组接字符串
System.out.println("==========joining==============");
String collect1 = emps.stream()
.map((e)->e.getName())
.collect(Collectors.joining());
System.out.println(collect1);
System.out.println("==========joining2==============");
String collect2 = emps.stream()
.map(Employee2::getName)
.collect(Collectors.joining(","));
System.out.println(collect2);
System.out.println("==========joining3==============");
String collect3 = emps.stream()
.map(Employee2::getName)
.collect(Collectors.joining(",", "前缀-", "-后缀"));
System.out.println(collect3);
System.out.println("==========maxBy==============");
Optional<Double> collect4 = emps.stream()
.map(Employee2::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(collect4.get());
System.out.println("==========minBy==============");
Optional<Double> collect5 = emps.stream()
.map(Employee2::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println(collect5);
}
/**
==========joining==============
李四张三王五赵六赵六赵六田七
==========joining2==============
李四,张三,王五,赵六,赵六,赵六,田七
==========joining3==============
前缀-李四,张三,王五,赵六,赵六,赵六,田七-后缀
==========maxBy==============
9999.99
==========minBy==============
Optional[3333.33]
**/
@Test
public void testCollect4() {
System.out.println("==========reducing==============");
Optional<Double> collect = emps.stream()
.map(Employee2::getSalary)
.collect(Collectors.reducing(Double::sum));
System.out.println(collect.get());
//分组
System.out.println("==========groupingBy==============");
Map<Employee2.Status, List<Employee2>> collect2 = emps.stream()
.collect(Collectors.groupingBy(Employee2::getStatus));
System.out.println(collect2);
//多级分组
System.out.println("==========groupingBy2==============");
Map<Employee2.Status, Map<String, List<Employee2>>> collect3 = emps.stream()
.collect(Collectors.groupingBy(Employee2::getStatus, Collectors.groupingBy((e)->{
if(e.getAge() >= 60) {
return "老年";
} else if(e.getAge() >= 35) {
return "中年";
} else {
return "成年";
}
})));
System.out.println(collect3);
//分区
System.out.println("==========partitioningBy==============");
Map<Boolean, List<Employee2>> collect4 = emps.stream()
.collect(Collectors.partitioningBy((e)->e.getSalary()>5000));
System.out.println(collect4);
}
/**
48888.84000000001
==========groupingBy==============
{BUSY=[Employee2 [id=102, name=李四, age=59, salary=6666.66, status=BUSY], Employee2 [id=104, name=赵六, age=8, salary=7777.77, status=BUSY], Employee2 [id=105, name=田七, age=38, salary=5555.55, status=BUSY]], VOCATION=[Employee2 [id=103, name=王五, age=28, salary=3333.33, status=VOCATION]], FREE=[Employee2 [id=101, name=张三, age=18, salary=9999.99, status=FREE], Employee2 [id=104, name=赵六, age=8, salary=7777.77, status=FREE], Employee2 [id=104, name=赵六, age=8, salary=7777.77, status=FREE]]}
==========groupingBy2==============
{BUSY={成年=[Employee2 [id=104, name=赵六, age=8, salary=7777.77, status=BUSY]], 中年=[Employee2 [id=102, name=李四, age=59, salary=6666.66, status=BUSY], Employee2 [id=105, name=田七, age=38, salary=5555.55, status=BUSY]]}, VOCATION={成年=[Employee2 [id=103, name=王五, age=28, salary=3333.33, status=VOCATION]]}, FREE={成年=[Employee2 [id=101, name=张三, age=18, salary=9999.99, status=FREE], Employee2 [id=104, name=赵六, age=8, salary=7777.77, status=FREE], Employee2 [id=104, name=赵六, age=8, salary=7777.77, status=FREE]]}}
==========partitioningBy==============
{false=[Employee2 [id=103, name=王五, age=28, salary=3333.33, status=VOCATION]], true=[Employee2 [id=102, name=李四, age=59, salary=6666.66, status=BUSY], Employee2 [id=101, name=张三, age=18, salary=9999.99, status=FREE], Employee2 [id=104, name=赵六, age=8, salary=7777.77, status=BUSY], Employee2 [id=104, name=赵六, age=8, salary=7777.77, status=FREE], Employee2 [id=104, name=赵六, age=8, salary=7777.77, status=FREE], Employee2 [id=105, name=田七, age=38, salary=5555.55, status=BUSY]]}
**/
Optional 类 (java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在用 Optional 可以更好的表达这个概念;Optional 类主要解决的问题就是空指针异常(NullPointerException)
常用方法:
@Test
public void test01(){
Optional<Employee> op = Optional.of(new Employee());
Employee employee = op.get();
Optional<Employee> op = Optional.empty();
Employee employee = op.get();
Optional<Employee> op = Optional.ofNullable(new Employee());
Employee employee = op.get();
Optional<Employee> op = Optional.ofNullable(new Employee());
if (op.isPresent()) {
Employee employee = op.get();
}
}
详细请看:Optional类 - 简书 (jianshu.com)
常用方法
@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());
}
它是以 Unix 元年 1970-01-01 00:00:00 到某个时间之间的毫秒值
@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);
}
@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());
}
@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);
}
@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);
}
@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);
// 上海时间
ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime shanghaiZonedDateTime = ZonedDateTime.now(shanghaiZoneId);
//在已构建好的日期时间上指定时区
LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
ZonedDateTime zdt1 = ldt2.atZone(ZoneId.of("Europe/Tallinn"));
System.out.println(zdt1);
}
public interface MyFun {
T func(int a);
default Integer getAge(){
return 22;
}
}
默认方法的”类优先”原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
(1)选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
1、新建一个接口 MyInterface1.java,里面有个默认实现方法 method1
public interface MyInterface1 {
default void method1() {
System.out.println("MyInterface1中的默认方法");
}
}
2、再建一个类 FatherClass.java,里面同样有一个同名的实现方法 method1
public class FatherClass {
public void method1() {
System.out.println("FatherClass中的方法method1");
}
}
3、创建一个子类 SonClass.java,这个类继承 FatherClass 父类,并且实现 MyInterface1 接口。
public class SonClass extends FatherClass implements MyInterface1{
}
4、测试这个子类创建后,调用 method1 方法,会调用哪个类或者接口中的实现方法?
@Test
public void test01() {
SonClass sc = new SonClass();
sc.method1();
}
/**
FatherClass中的方法method1
**/
5、结果:调用父类中的方法,并不是接口中的实现方法。
(2)接口冲突。如果一个接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突,子类必须指定覆盖哪个父接口中的方法。
1、新建一个 MyInterface2 接口,同 MyInterface1 接口中方法同名。
public interface MyInterface2 {
default void method1() {
System.out.println("MyInterface2中的默认方法");
}
}
2、新建 SonClass2 类,实现了 MyInterface1,MyInterface2 两个接口,这时会提醒你要实现哪个接口中的默认方法,如下:
public class SonClass2 implements MyInterface1,MyInterface2{
@Override
public void method1() {
MyInterface1.super.method1();//覆盖MyInterface1
}
}
3、测试
@Test
public void test02() {
SonClass2 sc = new SonClass2();
sc.method1();
}
4、结果:调用 MyInterface1 中的默认方法
Java8 中,接口中还允许添加静态方法
public interface MyInterface1 {
default void method1() {
System.out.println("MyInterface1中的默认方法");
}
public static void say() {
System.out.println("这是MyInterface1中的静态方法");
}
}
调用方式:接口名.方法名();
MyInterface1.say();
Java8 对注解处理提供了两点改进:可重复的注解及可用于类型的注解。
Java8 以前的版本使用注解有一个限制是相同的注解在同一位置只能使用一次,不能使用多次。
Java 8 引入了重复注解机制,这样相同的注解可以在同一地方使用多次。重复注解机制本身必须用 @Repeatable 注解。
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());
}
}
}
1、Java 8 的类型注解扩展了注解使用的范围。在 Java 8之前,注解只能是在声明的地方所使用,Java8 开始,注解可以应用在任何地方。
1.创建类实例
new @Interned MyObject();
2.类型映射
myString = (@NonNull String) str;
3.implements 语句中
class UnmodifiableList<T> implements@Readonly List<@Readonly T> { ... }
4.throw exception声明
void monitorTemperature() throws@Critical TemperatureException { ... }
2、新增 ElementType.TYPE_USE 和 ElementType.TYPE_PARAMETER(在Target上)
例如,下面的示例。
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@interface MyAnnotation {}