java8新特性
主要内容
1.Lambda表达式
2.函数式接口
3.方法引用与构造器引用
4.Stream API
5.接口中的默认方法与静态方法
6.新时间日期API
7.其他新特性
简介
- 速度更快
- 代码更少(增加了新的语法Lambda表达式)
- 强打的Stream API
- 便于并行
- 最大化减少空指针异常Optional
其中最为核心的为Lambda表达式与Stream API
map默认大小16
速度最快
hashmap:
占用的内存更少:
没有永久区,采用metaspace元空间(物理内存)
1、Lambda表达式
2、Java8 内置的四大核心函数式接口
@FunctionalInterface注解标注的都是函数式接口,可以作为相应的返回值,不一定都是java.util.function包下的。
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* java 内置的四大核心函数式接口
* 包名为:java.util.function
*
* Consumer<T>:消费性接口
* void accept(T t);
*
* Supplier<T>:供给型接口
* T get();
*
* Function<T,R> :函数型接口 T代表参数 R代表返回值
* R apply(T t);
*
* Predicate<T>:断言型接口
* boolean test(T t);
*/
public class LambdaFour {
//断言型接口
@Test
public void test4() {
List<String> strings = Arrays.asList("Hello", "aaa", "bbb", "sa");
List<String> strings1 = filterStr(strings, (s) -> s.length() > 3);
for (String s : strings1) {
System.out.println("s = " + s);
}
}
//需求:将满足条件的字符串放入集合中去
public List<String> filterStr(List<String> str, Predicate<String> pre){
List<String> strList = new ArrayList<>();
for (String st : str) {
if(pre.test(st)) {//判断st是否符合规定的条件
strList.add(st);
}
}
return strList;
}
//function<T,R>函数型接口
@Test
public void test3(){
String s = strHandler("\t\t\t 尚硅谷 ", (str) -> str.trim());
System.out.println("s = " + s);
}
//需求:用于处理字符串
public String strHandler(String str, Function<String, String> fun) {
return fun.apply(str);
}
//供给型接口 返回一些对象
@Test
public void test2() {
List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
for (Integer integer : numList) {
System.out.println("integer = " + integer);
}
}
//需求:产生指定个数的整数,并放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
//list.add(num);
Integer n = sup.get();
list.add(n);
}
return list;
}
//consumer<T> 消费型接口 无返回值
@Test
public void test1() {
happy(10000, (m)->System.out.println("汽车消费" + m + "元"));
happy(1, new Consumer(){
@Override
public void accept(Object o) {
System.out.println("o = " + o);
}
});
}
public void happy(double money, Consumer<Double> con) {
con.accept(money);
}
}
- 其他接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
BiFunction<T,U,R> | T,U | R | 对类型为T,U 参数应用操作,返回R类型的结果。包含方法为R apply(T t,U u) |
UnaryOperator(Function子接口) | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为T apply(T t); |
BinaryOperator(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) |
ToIntFunction ToLongFunction ToDoubleFunction | T | int long double | 分别计算int、long、double、值的函数 |
IntFunction LongFunction DoubleFunction | int long double | R | 参数分别为int、long、double类型的函数 |
3、方法引用与构造器引用、数组引用
- Lambda方法的进化版
- 一、方法引用:若
Lambda
体中的内容有方法已经实现了,我们可以使用“方法引用”(可以理解为方法引用时Lambda
表达式的另外一种表现形式)- 三种格式:
对象::实例方法名
类::静态方法名
类::实例方法名
- 注意:
Lambda
体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值保持一致!- 若
Lambda
参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
- 三种格式:
- 二、构造器引用
- 格式:
ClassName::new
- 注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致
- 格式:
- 三、数组引用
- 格式:
Type::new
- 格式:
- 一、方法引用:若
import org.junit.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.*;
public class MethodRef {
/**
*对象::实例方法名
*/
@Test
public void test1() {
Consumer<String> con = (x)-> System.out.println(x);//lambda方法体中已经有功能完成了
//前提void accept(T t)和println()方法和返回值类型保持一致
/**
* void accept(T t);
* public void println(String x) {
* synchronized (this) {
* print(x);
* newLine();
* }
* }
*/
PrintStream ps = System.out;
Consumer<String> con1 = ps::println;
con1.accept("兄啊年");
}
/**
* 对象::实例方法名
*/
@Test
public void test2() {
/**
* T get();
*/
Employee emp = new Employee("小明真帅!!!");
Supplier<String> sup = ()-> emp.getName();
System.out.println("sup.get() = " + sup.get());
//方法引用
Supplier<String> sup1 = emp::getName;
String sget = sup1.get();
System.out.println("sget = " + sget);
}
/**
* 类::静态方法名
*/
@Test
public void test3() {
// Comparator @FunctionalInterface
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
int compare = com.compare(2, 3);
System.out.println("compare = " + compare);
Comparator<Integer> com1 = Integer::compare;
int compare1 = com1.compare(4, 2);
System.out.println("compare1 = " + compare1);
}
/**
* 类::实例方法名
*/
@Test
public void test4() {
//比较两个字符串
/**
* boolean test(T t, U u);
* public boolean equals(Object anObject) {
*/
BiPredicate<String, String> bp = (x,y)-> x.equals(y);
//第一个参数是实例方法调用者
//第二个参数是实例方法参数
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("111", "111"));
}
@Test
public void test5() {
/**
* T get();
*/
Employee emp = new Employee();
Supplier<String> sup = ()-> emp.getName();
System.out.println(sup.get());
Supplier<Employee> sup1 = ()-> new Employee();
System.out.println(sup.get());
//无参构造器
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
}
@Test
public void test6() {
Function<String, Employee> fun = (x) -> new Employee(x);
System.out.println(fun.apply("hahh"));
//调用的有参构造,取决于Function的传入参数
/**
* R apply(T t);
*/
Function<String, Employee> fun1 = Employee::new;
System.out.println(fun1.apply("hahh"));
/**
*
* public interface BiFunction<T, U, R>
* R apply(T t, U u);
* 需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致!
*
*/
BiFunction<String, Integer, Employee> fun2 = (x, y) -> new Employee(x , y);
System.out.println(fun2.apply("小米糕", 10));
BiFunction<String, Integer, Employee> fun3 = Employee::new;
System.out.println(fun3.apply("小米糕123", 10));
}
/**
* 数组引用
*/
@Test
public void test7() {
Function<Integer, String[]> fun = (x)-> new String[x];
System.out.println(fun.apply(10).length);
/**
* Type::new
*/
Function<Integer, String[]> fun1 = String[]::new;
System.out.println(fun1.apply(20).length);
}
}
class Employee {
String name;
Integer age;
public Employee() {
}
public Employee(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Employee(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
4、强大的Stream API
4.1、了解Stream
Java8中有两大最为重要的改变。第一个是Lambda表达式
;另外一个则是Stream API(java.util.stream.*)
。
Stream
是Java8中处理集合的关键抽象概念,它可以指定你希望对集合
进行的操作,可以执行非常复杂的查找、过滤和映射数据
等操作。使用Stream API
来并行
执行操作。简而言之,Stream API
提供了一种高效且易于使用的处理数据的方式。
4.2、理解Stream
Stream是对数据源、集合、数组等进行一系列流水式的中间操作产生一个新流。
4.3、什么是Stream
-
流(Stream)到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
。“集合讲的是数据,流讲的是计算!” -
注意
- Stream 自己不会存储元素
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
4.4、Stream的操作三个步骤
- 创建 Stream
一个数据源(如:集合、数组),获取一个流 - 中间操作
一个中间操作链,对数据源的数据进行处理 - 终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
4.5、代码示例
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
1. 一、Stream的三个操作步骤
2. 1.创建Stream
3. 2.中间操作
4. 3.终止操作(终端操作)
*/
public class StreamApiOne {
//创建Stream
@Test
public void test1() {
// 1.可以通过Collection系列集合提供的Stream() 串行流或parallelStream()并行流
List<String> arrayList = new ArrayList();
Stream<String> stream1 = arrayList.stream();
//2.通过Arrays中的静态方法stream()获取数组流
Employee[] emps = new Employee[10];
Stream<Employee> stream2 = Arrays.stream(emps);
//3.通过stream类中的静态方法of()
Stream<Object> stream3 = Stream.of("aa", "bb", 1, 's');
//4.创建无限流
//迭代
//iterate(final T seed, final UnaryOperator<T> f)
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
stream4.limit(10).forEach(System.out::println);
//生成
Stream.generate(()-> Math.random())
.limit(5)
.forEach(System.out::println);
//终端操作
}
}
class Employee {
String name;
Integer age;
public Employee() {
}
public Employee(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Employee(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
4.6、Stream
多个中间操作可以连接起来形成一个流水线
,除非流水线上触发终止操作,否则中间操作不会执行任何的处理
!而在终止操作时一次性全部处理,称为"惰性求值"。
惰性延迟: 中间操作不执行,这个操作就叫惰性延迟
终止操作: 一次执行全部内容,即"惰性求值"
4.6.1、筛选与切片(中间操作)
方法 | 描述 |
---|---|
filter(Predicate p) | 接收Lambda,从流中排除某些元素 |
distinct() | 筛选、通过流所生成元素的hashCode()和equals()去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补 |
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator comp) | 产生一个新流,其中按比较器顺序排序 |
- filter
employees.stream()//创建Stream
.filter((x)-> x.getAge() > 15) //中间操作
.forEach(System.out::println); //终止操作
//输出结果:
/**
* Employee{name='小刚', age=16}
* Employee{name='小青', age=17}
* Employee{name='小青', age=17}
*/
Stream<StreamApiFive.Employee> employeeStream1 = employees.stream()
.filter((x) ->{
System.out.println("Stream API的中间操作"); //中间操作也叫内部迭代
return x.getAge() > 12;
} );
//输出结果:
// 不输出
//外部迭代
Iterator<Employee> iterator = employees.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
- limit
@Test
public void test2() {
employees.stream()//创建Stream
.filter((x)-> x.getAge() > 18) //中间操作
.limit(2)
.forEach(System.out::println); //终止操作
}
- skip
@Test
public void test3() {
/**
* "短路" = 短路
* "短路" = 短路
* Employee{name='小红', age=15}
* "短路" = 短路
* Employee{name='小刚', age=16}
* "短路" = 短路
* Employee{name='小青', age=17}
* "短路" = 短路
* Employee{name='小青', age=18}
*/
System.out.println("-----------------初始值--------------------------");
employees.stream()
.filter((x)-> {
System.out.println("\"短路\" = " + "短路");
return x.age > 12;
})
.forEach(System.out::println);
/**
* "短路" = 短路
* "短路" = 短路
* "短路" = 短路
* "短路" = 短路
* Employee{name='小青', age=17}
* "短路" = 短路
* Employee{name='小青', age=17}
*/
System.out.println("------------------跳过前skip(n) n个元素-------------------------");
employees.stream()
.filter((x)-> {
System.out.println("\"短路\" = " + "短路");
return x.age > 12;
})
.skip(2)
.forEach(System.out::println);
System.out.println("------------------当返回的结果不满足skip(n) n中的个数时,返回空流------------------------");
employees.stream()
.filter((x)-> {
System.out.println("\"短路\" = " + "短路");
return x.age > 17;
})
.skip(2)
.forEach(System.out::println);
}
- distinct
@Test
public void test5() {
/**
* 初始结果
Employee{name='小明', age=12}
Employee{name='小红', age=15}
Employee{name='小刚', age=16}
Employee{name='小青', age=17}
Employee{name='小青', age=17}
*/
employees.stream()
.distinct()
.forEach(System.out::println);
//类 去重 需要重写hashcode 和 equals
/**
*Employee{name='小明', age=12}
* Employee{name='小红', age=15}
* Employee{name='小刚', age=16}
* Employee{name='小青', age=17}
*/
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 5, 5, 5, 6, 7);
List<Integer> collect = numbers.stream()
.distinct().collect(Collectors.toList());
collect.forEach(System.out::print);
//结果:1234567
}
- 前面代码全部
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
/**
* 一、Stream的三个操作步骤
* 1.创建Stream
* 2.中间操作
* 3.终止操作(终端操作)
*/
public class StreamApiTrwo {
List<Employee> employees = Arrays.asList(
new Employee("小明", 12),
new Employee("小红", 15),
new Employee("小刚", 16),
new Employee("小青", 17),
new Employee("小青", 17)
);
@Test
public void test1() {
//这里的x表示employees对象
Stream<Employee> employeeStream = employees.stream()
.filter((x) -> x.getAge() > 16);
//Employee{name='小青', age=17}
employeeStream.forEach(System.out::println);
//中间操作不执行
//这个操作就叫惰性延迟 终止操作
Stream<Employee> employeeStream1 = employees.stream()
.filter((x) ->{
System.out.println("Stream API的中间操作");
return x.getAge() > 12;
} );
//终止操作:一次执行全部内容,即"惰性求值"
/**
* Stream API的中间操作
* Stream API的中间操作
* Stream API的中间操作
* Stream API的中间操作
* Employee{name='小青', age=17}
*/
employeeStream1.forEach(System.out::println);
}
@Test
public void test2() {
/*employees.stream()
.filter((x)-> x.age > 14)
.limit(2)
.forEach(System.out::println);*/
/**
* 短路
* 短路
* Employee{name='小红', age=15}
* 短路
* Employee{name='小刚', age=16}
*/
employees.stream()
.filter((x)-> {
System.out.println("短路");
return x.age > 12;
})
.limit(2)
.forEach(System.out::println);
}
@Test
public void test3() {
employees.stream()
.filter((x)-> {
System.out.println("短路");
return x.age > 12;
})
.skip(2)
.forEach(System.out::println);
}
@Test
public void test4() {
//distinct 需要重写hashcode 和 equals
employees.stream()
.filter((x)-> {
System.out.println("短路");
return x.age > 12;
})
.distinct()
.forEach(System.out::println);
}
@Test
public void test5() {}
class Employee {
String name;
Integer age;
public Employee() {
}
public Employee(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Employee(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(name, employee.name) &&
Objects.equals(age, employee.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
}
4.6.2、映射(中间操作)
映射:
map:接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
map参数对象 Stream map(Function<? super T, ? extends R> mapper
);
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。flatMap
Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper
)
- 案例一:自定义Stream ——Stream filterCharacter(String str)
@Test
public void test5() {
//map
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
list.stream()
.map((str)->str.toUpperCase())
.forEach(System.out::println);
//map转换成其他流
//<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Stream<Stream<Character>> streamStream = list.stream()
//任意
.map(StreamApiMapAndFlatMap::filterCharacter);//{{a,a,a}, {b,b,b}}
streamStream.forEach((s)->{
s.forEach(System.out::println);
});
System.out.println("---------------------filterMap---------------------");
//filterMap stream流对象
//<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
list.stream()
//参数是Stream<T>
.flatMap(StreamApiMapAndFlatMap::filterCharacter)//{a,a,a,b,b,b} 参数是Stream<T>
.forEach(System.out::println);
}
public static Stream<Character> filterCharacter(String str) {
List<Character> list = new ArrayList<>();
for (Character c: str.toCharArray()) {
list.add(c);
}
return list.stream();
}
- 案例二:集合、数组、数据源的Stream
@Test
public void test(){
List<String> stringList = Arrays.asList("hello", "world");
List<String> collect1 = stringList.stream()
//.map(str -> str.split(""))
.distinct().collect(Collectors.toList());
collect1.forEach(System.out::println);
/**
* hello
* world
*/
List<String[]> collect = stringList.stream()
.map(str -> str.split(""))
.distinct().collect(Collectors.toList());
collect.forEach(col-> System.out.println(Arrays.toString(col)));
/**
* [h, e, l, l, o]
* [w, o, r, l, d]
*/
List<String> stringList1 = Arrays.asList("hello", "world");
stringList1.stream()
.map(str -> str.split(""))
.flatMap(Arrays::stream)
.distinct()
.forEach(System.out::println);
/**
* h
* e
* l
* o
* w
* r
* d
*/
}
- 案例三 list中实体类的两个字段转为Map
List<User> list = new ArrayList<>();
Map<Integer, String> map = list.stream().collect(Collectors.toMap(User::getId, User::getType));
map:将整个元素添加到集合中
filtermap:将流中的元素一个一个添加到集合中
4.6.3、排序(中间操作)
sorted: 自然排序
sorted(Comparator com): 定制排序
/**
* 排序
* sorted()——自然排序(Comparable)
* sorted(Comparator com)——定制排序(Comparator)
*/
@Test
public void test7() {
List<String> list = Arrays.asList( "bbb", "aaa","ccc", "ddd");
list.stream()
.sorted()
.forEach(System.out::println);
System.out.println("-------------------");
//年龄相同按名字排序,否则按名字排序
employees.stream()
//int compare(T o1, T o2);
.sorted((x,y)->{
if (x.getAge().equals(y.getAge())) {
return x.getName().compareTo(y.getName());
} else {
return x.getAge().compareTo(y.getAge());
}
})
.forEach(System.out::println);
}
4.6.4、Stream的查询与匹配
- 查找与匹配
- allMatch——检查是否匹配所有元素
- anyMatch——检查是否至少匹配一个元素
- noneMatch——检查是否没有匹配所有元素
- findFirst——返回第一个元素
- findAny——返回当前流中的任意元素
- count——返回流中元素的总个数
- max——返回流中的最大值
- min——返回流中的最小值
//allMatch 是否全都匹配
boolean b = employees.stream()
.allMatch((e) -> e.getAge() > 11);//看看是否所有的数据都符合这个条件
System.out.println("b = " + b);
//至少有一个元素匹配
boolean b1 = employees.stream()
.anyMatch((e) -> e.getAge() > 15);
System.out.println("b1 = " + b1);//至少有一个条件符合这个条件
//noneMatch 查看是否有没有匹配的元素
boolean b2 = employees.stream()
.noneMatch((e) -> e.getAge() > 90);
System.out.println("b2 = " + b2);
//findFirst 返回第一个元素
long l = employees.stream()
.findFirst()//Optional
.get()
.age;
System.out.println("l = " + l);
//count
System.out.println("employees.stream().count() = " + employees.stream().count());
//max
Optional<Employee> max = employees.stream().max((x, y) -> x.getAge() - y.getAge());
System.out.println("max = " + max.get());
//min
Optional<Employee> min = employees.stream().min((x, y) -> x.getAge() - y.getAge());
//min.orElse() 有可能为空封装数据到Optional中
System.out.println("min = " + min);
//findAny
String l1 = employees.stream()
.findAny()
.get()
.getName();
System.out.println("l1 = " + l1);
4.6.5、归约与收集
- 归约
- reduce(T iden,BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回T
- reduce(BinaryOperator b): 可以将流中元素反复结合起来,得到一个值。返回
Optional<T>
- 备注:
map
和reduce
的连接通常称为map-reduce模式
,因Google
用它来进行网络搜索而出名。
@Test
public void test2() {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
// T reduce(T identity, BinaryOperator<T> accumulator);
// identity 起始值 BinaryOperator二元运算
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println("sum = " + sum);
System.out.println("---------------------------");
//工资的总额
Optional<Integer> reduce = employees.stream()
.map(Employee::getAge)
.reduce(Integer::sum);//结果累加操作
}
- 收集
- collect(Collector c):将流转换为其他形式。接收一个
Collector
接口的实现,用于给Stream
中元素做汇总的方法。 Collector
接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map
)。但是Collectors
实用类提供了很多静态方法,可以方便地创建常见收集器实例。
- collect(Collector c):将流转换为其他形式。接收一个
@Test
public void test4() {
List<String> collect = employees.stream()
.map(Employee::getName)
.collect(Collectors.toList());
collect.forEach(System.out::println);
System.out.println("------------------------");
Set<String> set = employees.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("------------------------");
HashSet<String> hashSet = employees.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hashSet.forEach(System.out::println);
System.out.println("------------------------");
}
@Test
public void test5() {
//总数
Long count = employees.stream()
.collect(Collectors.counting());
System.out.println("count = " + count);
System.out.println("---------------");
//平均值
Double collect = employees.stream()
.collect(Collectors.averagingDouble(Employee::getAge));
System.out.println("collect = " + collect);
//总和
DoubleSummaryStatistics collect1 = employees.stream()
.collect(Collectors.summarizingDouble(Employee::getAge));
System.out.println("collect1 = " + collect1);
//最大值
Optional<Employee> max = employees.stream()
.collect(Collectors.maxBy((x, y) -> Integer.compare(x.getAge(), y.getAge())));
System.out.println("max.get() = " + max.get());
//最小值
Optional<Employee> min = employees.stream()
.collect(Collectors.minBy((x, y) -> Integer.compare(x.getAge(), y.getAge())));
System.out.println("min.get() = " + min.get());
Optional<Integer> minOpt = employees.stream()
.map(Employee::getAge)
.collect(Collectors.minBy(Integer::compare));
System.out.println("minOpt = " + minOpt.get());
/**
* collect2 = {LAONIAN=[Employee{name='小青', age=77}, Employee{name='小青', age=87}],
* QINGNIAN=[Employee{name='小红', age=23}, Employee{name='小刚', age=23}],
* YOUNG=[Employee{name='小明', age=12}]}
*/
//分组
Map<Employee.Status, List<Employee>> collect2 = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
collect2.keySet().forEach(System.out::println);
collect2.values().forEach(System.out::println);
Iterator<Map.Entry<Employee.Status, List<Employee>>> iterator = collect2.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Employee.Status, List<Employee>> next = iterator.next();
Employee.Status key = next.getKey();
List<Employee> employees = collect2.get(key);
System.out.println("key = " + key);
System.out.println("employees = " + employees);
}
System.out.println("collect2 = " + collect2);
}
//多级分组
@Test
public void test6() {
Map<Employee.Status, Map<String, List<Employee>>> collect = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if (e.getAge() <= 35) {
return "青年";
} else if (e.getAge() <= 50) {
return "中年";
} else {
return "老年";
}
})));
System.out.println("collect = " + collect);
}
//分区
@Test
public void test7() {
/**
* collect = {
* false=[Employee{name='小明', age=12}, Employee{name='小红', age=23}, Employee{name='小刚', age=23}],
* true=[Employee{name='小青', age=77}, Employee{name='小青', age=87}]}
*/
Map<Boolean, List<Employee>> collect = employees.stream()
.collect(Collectors.partitioningBy((e) -> e.getAge() > 35));
System.out.println("collect = " + collect);
}
//
@Test
public void test8() {
IntSummaryStatistics collect = employees.stream()
.collect(Collectors.summarizingInt(Employee::getAge));
System.out.println("collect.getAverage() = " + collect.getAverage());
System.out.println("collect.getCount() = " + collect.getCount());
System.out.println("collect.getMax() = " + collect.getMax());
System.out.println("collect.getMin() = " + collect.getMin());
System.out.println("collect.getSum() = " + collect.getSum());
}
//连接
@Test
public void test9() {
//链接
String collect = employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(","));
//小明,小红,小刚,小青,小青
System.out.println("collect = " + collect);
}
5、并行流与串行流
5.1、了解Fork/Join框架
Fork/Join框架:就是在必要的情况下,将一个大任务,将进行 拆分(fork
)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行join
汇总。
5.2、Fork/Join框架与传统线程池的区别
采用“工作窃取
”模式(work-stealing
):
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放到自己的队列中。
相对于一般的线程池实现fork/join框架
的优势体现在对其中包含的任务的处理方式上。在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态。而在fork/join框架
实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行。这种方式减少了线程的等待时间,提高了性能。
- ForkJoinCalculate
package com.cjw.forkjoin;
import java.util.concurrent.RecursiveTask;
public class ForkJoinCalculate extends RecursiveTask<Long> {
private static final long serialVersionUID = 4545645645156456L;
private long start;
private long end;
public ForkJoinCalculate(long start, long end) {
this.start = start;
this.end = end;
}
private static final long THRESHOLD = 10000;
/**
*
* @return
*/
@Override
protected Long compute() {
long length = end - start;
if (length <= THRESHOLD) {
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
} else {
long middle = (start + end)/2;
ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
left.fork();//拆分子任务,同时加入线程队列
ForkJoinCalculate right = new ForkJoinCalculate(start, middle);
right.fork();//拆分子任务,同时加入线程队列
return left.join() + right.join();
}
}
}
- TestForkJoin
package com.cjw.forkjoin;
import org.junit.Test;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
/**
*
*/
public class TestForkJoin {
@Test
public void test1() {
Instant start = Instant.now();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> 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).toMillis());
}
/**
* 普通for
*/
@Test
public void test2() {
Instant start = Instant.now();
long sum = 0l;
for (long i = 0; i < 10000000L; i++) {
sum += i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());
}
/**
* java8 并行流
*/
@Test
public void test3() {
Instant start = Instant.now();
LongStream.rangeClosed(0, 100000000000l)
.parallel()
.reduce(0, Long::sum);
Instant end = Instant.now();
System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());
}
}
5.3、并行流与顺序流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java 8中将并行进行了优化,我们可以很容易的对数据进行并行操作。StreamAPI
可以声明性地通过parallel()与sequential()
在并行流与顺序流
之间进行切换。
6、Optional类
Optional<T>类(java.util.Optional)
是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在Optional
可以更好的表达这个概念。并且避免空指针异常。
常用方法:
- Optional.of(T t):创建一个
Optional
实例 - Optional.empty():创建一个空的
Optional
实例 - Optional.ofNullable(T t):若t不为null,创建
Optional
实例,否则创建空实例 - isPresent():判断是否包含值。
- orElse(T t):如果调用对象包含值,返回该值,否则返回t
- orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值
- map(Function f):如果有值对其处理,并返回处理后的
Optional
,否则返回Optional.empty()
; - flatMap(Function mapper):与
map
类似,要求返回值必须是Optional
7、接口中的默认方法与静态方法
7.1、接口中的默认方法
接口默认方法的“类优先”原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
- 选择父类中的方法: 如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
- 接口冲突: 如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法类解决冲突。
7.2、类优先实例
- MyFun
public interface MyFun {
default String getName() {
return "哈哈哈哈";
}
}
- MyClass
public class MyClass {
public String getName() {
return "嘿嘿嘿";
}
}
- SubClass
public class SubClass extends MyClass implements MyFun {
public static void main(String[] args) {
SubClass sc = new SubClass();
System.out.println(sc.getName());
}
}
结果:
7.3、接口冲突
- 增加MyInterface
public interface MyInterface {
default String getName() {
return "哈哈哈哈";
}
}
- SubClass
public class SubClass /*extends MyClass*/ implements MyFun,MyInterface {
public static void main(String[] args) {
SubClass sc = new SubClass();
System.out.println(sc.getName());
}
@Override
public String getName() {
//return MyFun.class.getName();
return new MyFun() {
@Override
public String getName() {
return "this is MyFun";
}
}.getName();
}
}
7.4、接口中的静态方法
- SubClass
public class SubClass /*extends MyClass*/ implements MyFun,MyInterface {
public static void main(String[] args) {
MyInterface.show();
}
@Override
public String getName() {
return null;
}
}
- MyInterface
public interface MyInterface {
default String getName() {
return "哈哈哈哈";
}
public static void show() {
System.out.println("接口中的静态方法");
}
}
8、新时间日期API
区别:Date Calender(加减时间、每周第一天、线程安全)
TimeZone
8.1、 传统时间格式化的线程安全问题
- 线程不安全–SimpleDateFormat
public static void main(String[] args) throws ExecutionException, InterruptedException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return sdf.parse("20161218");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<Date> result : results) {
System.out.println("result.get() = " + result.get());
}
}
- 线程安全–ThreadLocal 和 DateFormatThreadLocal
public class DateFormatThreadLocal {
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyyMMdd");
}
};
public static Date convert(String source) throws ParseException {
return df.get().parse(source);
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return DateFormatThreadLocal.convert("20161218");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<Date> result : results) {
System.out.println("result.get() = " + result.get());
}
pool.shutdown();
}
- 线程安全DateTimeFormatter
public static void main(String[] args) throws ExecutionException, InterruptedException {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
Callable<LocalDate> task = new Callable<LocalDate>() {
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("20161218", dtf);
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<LocalDate>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<LocalDate> result : results) {
System.out.println("result.get() = " + result.get());
}
pool.shutdown();
8.2、本地时间与时间戳
- LocalDateTime
public static void main(String[] args) throws ExecutionException, InterruptedException {
LocalDateTime ldt1 = LocalDateTime.now();//显示当前时间
System.out.println("ldt1 = " + ldt1);
LocalDateTime ldt2 = LocalDateTime.of(2015, 4, 1, 1, 2);//指定日期显示时间
System.out.println("ldt2 = " + ldt2);
LocalDateTime ldt3 = ldt1.plusYears(2);//时间 +2 year
System.out.println("ldt3 = " + ldt3);
LocalDateTime ldt4 = ldt1.minusHours(1);//时间 -2 hour
System.out.println("ldt4 = " + ldt4);
System.out.println(ldt1.getYear());
System.out.println(ldt1.getMonthValue());
System.out.println(ldt1.getDayOfMonth());
System.out.println(ldt1.getHour());
LocalDateTime ldt11 = LocalDateTime.now(Clock.systemUTC());
System.out.println("ldt11 = " + ldt11);
LocalDateTime ldt12 = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
System.out.println("ldt12 = " + ldt12);
}
- Instant
UTC:格林威治时间
public static void main(String[] args) throws ExecutionException, InterruptedException {
Instant ins1 = Instant.now();
System.out.println("ins1 = " + ins1);
OffsetDateTime odt = ins1.atOffset(ZoneOffset.ofHours(8));
System.out.println("odt = " + odt);
System.out.println("ins1.toEpochMilli() = " + ins1.toEpochMilli());
Instant ins2 = Instant.ofEpochSecond(60);//从1970-01-01T00:00:00Z开始计算
System.out.println("ins2 = " + ins2);
}
-
LocatDateTime和Instant区别
LocalDateTime
它表示的是不带时区的 日期及时间,替换之前的Calendar
。看上去,LocalDateTime和Instant
很象,但记得的是“Instant
中是不带时区的即时时间点。可能有人说,即时的时间点 不就是日期+时间么?看上去是这样的,但还是有所区别,比如LocalDateTime
对于用户来说,可能就只是一个简单的日期和时间的概念,考虑如下的 例子:两个人都在2013年7月2日11点出生,第一个人是在英国出生,而第二个是在加尼福利亚,如果我们问他们是在什么时候出生的话,则他们看上去都是 在同样的时间出生(就是LocalDateTime
所表达的),但如果我们根据时间线(如格林威治时间线)去仔细考察,则会发现在出生的人会比在英国出生的人稍微晚几个小时(这就是Instant
所表达的概念,并且要将其转换为UTC
格式的时间)。 -
Duration 和 Period
- Duration: 计算两个“
时间
”之间的间隔 - Period:计算两个“
日期
”之间的间隔
public static void main(String[] args) throws ExecutionException, InterruptedException {
Instant ins1 = Instant.now();
Thread.sleep(1000);
Instant ins2 = Instant.now();
Duration between = Duration.between(ins1, ins2);
System.out.println(between.toMillis());
LocalDate ld1 = LocalDate.of(2015, 1, 1);
LocalDate ld2 = LocalDate.now();
Period between1 = Period.between(ld1, ld2);
System.out.println(between1);
System.out.println(between1.getYears());
System.out.println(between1.getMonths());
System.out.println(between1.getDays());
}
8.3、时间校正器
- TemporalAdjuster: 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作
- TemporalAdjusters: 该类通过静态方法提供了大量的常用TemporalAdjuster的实现。
public static void main(String[] args) throws ExecutionException, InterruptedException {
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println("ldt1 = " + ldt1);
LocalDateTime ldt2 = ldt1.withDayOfMonth(10);
System.out.println("ldt2 = " + ldt2);
LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println("ldt3 = " + ldt3);
//自定义
LocalDateTime ldt5 = ldt1.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 = " + ldt5);
}
8.4、时间格式化与时区处理
- DateTimeFormatter
public static void main(String[] args) throws ExecutionException, InterruptedException {
DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE;
LocalDateTime ldt = LocalDateTime.now();
String strDate = ldt.format(dtf);
System.out.println("strDate = " + strDate);
System.out.println("--------------------------------------");
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String strDate2 = dtf2.format(ldt);
System.out.println(strDate2);
LocalDateTime newDate = LocalDateTime.parse(strDate2, dtf2);
System.out.println("newDate = " + newDate);
}
- 时区处理
Java8
中加入了对时区的支持,带时区的时间为`ZonedDateTime`` 其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式 例如:Asia/Shanghai等ZoneId
:该类中包含了所有的时区信息getAvailableZoneIds()
:可以获取所有时区时区信息of(id)
: 用指定的时区信息获取ZoneId对象
public static void main(String[] args) throws ExecutionException, InterruptedException {
LocalDateTime ldt = LocalDateTime.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT")));
System.out.println("ldt = " + ldt);
LocalDateTime ldt2 = LocalDateTime.now();
ZonedDateTime zdt = ldt2.atZone(ZoneId.of("Europe/Tallinn"));
System.out.println("zdt = " + zdt);
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(System.out::println);
}
9、重复注解与类型注解
Java8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。
- MyAnnotations
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER,
ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
- MyAnnotation
@Repeatable(MyAnnotations.class)
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER,
ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
- TestAnnotation——重复注解
@Test
public void test1() throws NoSuchMethodException {
Class<TestAnnotation> clazz = TestAnnotation.class;
Method m1 = clazz.getMethod("show");
MyAnnotation[] mas = m1.getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation ma : mas) {
System.out.println(ma.value());
}
}
@MyAnnotation("hello")
@MyAnnotation("world")
public void show(@MyAnnotation("abc") String str){
}
- TestAnnotation——类型注解
@MyAnnotation("hello")
@MyAnnotation("world")
public void show1(@MyAnnotation("abc") String str){
}
@Test
public void test2() throws NoSuchMethodException {
Class<TestAnnotation> clazz = TestAnnotation.class;
Method m1 = clazz.getMethod("show1", String.class);
MyAnnotation[] mas = m1.getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation ma : mas) {
System.out.println(ma.value());
}
}