目录结构:
目录
Java 8新特性简介
1. Lambda 表达式
2. 函数式接口
3. 方法引用与构造器引用
4. Stream API
5. Optional 类
6. 接口中的默认方法与静态方法
7. 新时间日期 API
8.注解的新特性
8. 集合的底层源码实现
9. 其他新特性
Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。
代码更少(增加了新的语法:Lambda 表达式)
强大的 Stream API
速度更快
最大化减少空指针异常:Optional容器类
Nashorn引擎,允许在JVM上运行JS应用
便于并行
修改底层数据结构:如HashMap(数组-链表-红黑树),HashSet,ConcurrentHashMap(CAS算法)
修改垃圾回收机制:取消堆中的永久区(PremGen)->回收条件苛刻,使用元空间(MetaSpace)->直接使用物理内存->加载类文件
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
从匿名类到 Lambda 的转换
//匿名内部类
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("hello world!");
}
};
//Lambda 表达式
Runnable r2 = ()->System.out.println("hello Lambda!");
//原来使用匿名内部类作为参数传递
TreeSet ts = new TreeSet<>(new Comparator() {
@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(), o2.length());
}
});
//Lambda 表达式作为参数传递
TreeSet ts2 = new TreeSet<>(
(o1,o2)->Integer.compare(o1.length(), o2.length())
);
Lambda 表达式在Java 语言中引入了一个新的语法元 素和操作符。这个操作符为 “->” , 该操作符被称 为 Lambda 操作符或剪头操作符。它将 Lambda 分为 两个部分:
左侧:指定了 Lambda 表达式需要的所有参数
右侧:指定了 Lambda 体,即 Lambda 表达式要执行 的功能。
所有代码:
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.junit.Test;
/*
* 一、Lambda 表达式的基础语法:Java8中引入了一个新的操作符 "->" 该操作符称为箭头操作符或 Lambda 操作符
* 箭头操作符将 Lambda 表达式拆分成两部分:
*
* 左侧:Lambda 表达式的参数列表
* 右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体
*
* 语法格式一:无参数,无返回值
* () -> System.out.println("Hello Lambda!");
*
* 语法格式二:有一个参数,并且无返回值
* (x) -> System.out.println(x)
*
* 语法格式三:若只有一个参数,小括号可以省略不写
* x -> System.out.println(x)
*
* 语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
* Comparator com = (x, y) -> {
* System.out.println("函数式接口");
* return Integer.compare(x, y);
* };
*
* 语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
* Comparator com = (x, y) -> Integer.compare(x, y);
*
* 语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
* (Integer x, Integer y) -> Integer.compare(x, y);
*
* 上联:左右遇一括号省
* 下联:左侧推断类型省
* 横批:能省则省
*
*左边只有一个参数,遇到括号就可以省略,右边函数只有一条语句,大括号可以省略
*表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
*
* 二、Lambda 表达式需要“函数式接口”的支持
* 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。 可以使用注解 @FunctionalInterface 修饰
* 可以检查是否是函数式接口
*/
public class TestLambda2 {
@Test
public void test1(){
int num = 0;//jdk 1.7 前,必须是 final
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!" + num);
}
};
r.run();
System.out.println("-------------------------------");
Runnable r1 = () -> System.out.println("Hello Lambda!");
r1.run();
}
@Test
public void test2(){
Consumer con = x -> System.out.println(x);
con.accept("我大尚硅谷威武!");
}
@Test
public void test3(){
Comparator com = (x, y) -> {
System.out.println("函数式接口");
return Integer.compare(x, y);
};
}
@Test
public void test4(){
Comparator com = (x, y) -> Integer.compare(x, y);
}
//类型推断
@Test
public void test5(){
String[] strs= {"aaa", "bbb", "ccc"};
// String[] strs;
// strs = {"aaa", "bbb", "ccc"};
List list = new ArrayList<>();
//java8 还以这样 这在jdk1.7是不可以的
show(new HashMap<>());
}
public void show(Map map){
}
//需求:对一个数进行运算
@Test
public void test6(){
Integer num = operation(100, (x) -> x * x);
System.out.println(num);
System.out.println(operation(200, (y) -> y + 200));
}
public Integer operation(Integer num, MyFun mf){
return mf.getValue(num);
}
}
上述 Lambda 表达式中的参数类型都是由编译器推断 得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的 “类型推断”。
语法更加简洁:
import java.util.Comparator;
import java.util.TreeSet;
import org.junit.Test;
public class TestLambda1 {
//原来的匿名内部类
@Test
public void test1(){
Comparator com = new Comparator(){
@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(), o2.length());
}
};
TreeSet ts = new TreeSet<>(com);
TreeSet ts2 = new TreeSet<>(new Comparator(){
@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(), o2.length());
}
});
}
//现在的 Lambda 表达式
@Test
public void test2(){
Comparator com = (x, y) -> Integer.compare(x.length(), y.length());
TreeSet ts = new TreeSet<>(com);
}
}
Employee 类:
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(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 + "]";
}
}
案例:获取公司中年龄小于等于 35 的员工信息、获取公司中工资大于等于 5000 的员工信息
以前的方式肯定值通过写一个方法循环遍历即可
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
public class TestLambda1 {
List emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
//需求:获取公司中年龄小于等于 35 的员工信息
public List filterEmployeeAge(List emps){
List list = new ArrayList<>();
for (Employee emp : emps) {
if(emp.getAge() <= 35){
list.add(emp);
}
}
return list;
}
//需求:获取公司中工资大于等于 5000 的员工信息
public List filterEmployeeSalary(List emps){
List list = new ArrayList<>();
for (Employee emp : emps) {
if(emp.getSalary() >= 5000){
list.add(emp);
}
}
return list;
}
@Test
public void test3(){
System.out.println("-------年龄小于等于35 的员工信息-------");
List list = filterEmployeeAge(emps);
for (Employee employee : list) {
System.out.println(employee);
}
System.out.println("=================================================");
System.out.println("-------工资大于等于 5000 的员工信息-------");
List list1 = filterEmployeeSalary(emps);
for (Employee employee : list1) {
System.out.println(employee);
}
}
}
-------年龄小于等于 35 的员工信息-------
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]
=================================================
-------工资大于等于 5000 的员工信息-------
Employee [id=101, name=张三, age=18, salary=9999.99]
Employee [id=102, name=李四, age=59, salary=6666.66]
Employee [id=104, name=赵六, age=8, salary=7777.77]
Employee [id=105, name=田七, age=38, salary=5555.55]
创建抽象类 MyPredicate ,抽象内中有一个方法 test ,通过传入的参数返回 true 或 false
@FunctionalInterface
public interface MyPredicate {
public boolean test(T t);
}
MyPredicate 抽象类的实现类 FilterEmployeeForAge:
public class FilterEmployeeForAge implements MyPredicate{
@Override
public boolean test(Employee t) {
return t.getAge() <= 35;
}
}
MyPredicate 抽象类的实现类 FilterEmployeeForSalary:
public class FilterEmployeeForSalary implements MyPredicate {
@Override
public boolean test(Employee t) {
return t.getSalary() >= 5000;
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
public class TestLambda1 {
List emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
//优化方式一:策略设计模式
public List filterEmployee(List emps, MyPredicate mp){
List list = new ArrayList<>();
for (Employee employee : emps) {
if(mp.test(employee)){
list.add(employee);
}
}
return list;
}
@Test
public void test4(){
List list = filterEmployee(emps, new FilterEmployeeForAge());
System.out.println("-------年龄小于等于 35 的员工信息-------");
for (Employee employee : list) {
System.out.println(employee);
}
System.out.println("=================================================");
System.out.println("-------工资大于等于 5000 的员工信息-------");
List list2 = filterEmployee(emps, new FilterEmployeeForSalary());
for (Employee employee : list2) {
System.out.println(employee);
}
}
}
上面的策略设计模式每次都还要去创建一个实现类,有点麻烦,使用匿名内部类可以解决这个问题。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
public class TestLambda1 {
List emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
//优化方式一:策略设计模式
public List filterEmployee(List emps, MyPredicate mp){
List list = new ArrayList<>();
for (Employee employee : emps) {
if(mp.test(employee)){
list.add(employee);
}
}
return list;
}
//优化方式二:匿名内部类
@Test
public void test5(){
List list = filterEmployee(emps, new MyPredicate() {
@Override
public boolean test(Employee t) {
return t.getAge() <= 35;
}
});
System.out.println("-------年龄小于等于 35 的员工信息-------");
for (Employee employee : list) {
System.out.println(employee);
}
System.out.println("=================================================");
List list1 = filterEmployee(emps, new MyPredicate() {
@Override
public boolean test(Employee t) {
return t.getSalary() >= 5000;
}
});
System.out.println("-------工资大于等于 5000 的员工信息-------");
for (Employee employee : list1) {
System.out.println(employee);
}
}
}
使用 Lambda可以大大简化上面的操作
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
public class TestLambda1 {
List emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
public List filterEmployee(List emps, MyPredicate mp){
List list = new ArrayList<>();
for (Employee employee : emps) {
if(mp.test(employee)){
list.add(employee);
}
}
return list;
}
//优化方式三:Lambda 表达式
@Test
public void test6(){
List list = filterEmployee(emps, (e) -> e.getAge() <= 35);
System.out.println("-------年龄小于等于 35 的员工信息-------");
list.forEach(System.out::println);
System.out.println("=================================================");
List list2 = filterEmployee(emps, (e) -> e.getSalary() >= 5000);
System.out.println("-------工资大于等于 5000 的员工信息-------");
list2.forEach(System.out::println);
}
}
是不是还想减少代码量 ,体验一下 Stream API,使用Stream API filterEmployee函数都不需要了。
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
public class TestLambda1 {
List emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
//优化方式四:Stream API
@Test
public void test7(){
System.out.println("-------年龄小于等于 35 的员工信息-------");
emps.stream()
.filter((e) -> e.getAge() <= 35)
.forEach(System.out::println);
System.out.println("=================================================");
emps.stream()
.map(Employee::getName)
.limit(3)
.sorted()
.forEach(System.out::println);
}
}
练习:
1. 调用 Collections.sort() 方法,通过定制排序比较两个 Employee(先按年龄比,年龄相同按姓名比),使用 Lambda作为参数传递。
2.
①声明函数式接口,接口中声明抽象方法,public String getvalue(String str);
②声明类 TestLambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值。
③再将一个字符串的第2个和第4个索引位置进行截取子串。
3.
①声明一个带两个泛型的函数式接口,泛型类型为
②接口中声明对应抽象方法
③在 Testlambda类中声明方法,使用接口作为参数,计算两个long型参数的和。
④再计算两个long型参数的乘积。
创建函数式接口:MyFunction MyFunction2
MyFunction
@FunctionalInterface
public interface MyFunction {
public String getValue(String str);
}
MyFunction2
public interface MyFunction2 {
public R getValue(T t1, T t2);
}
TestLambda
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import com.atguigu.java8.Employee;
public class TestLambda {
List emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
@Test
public void test1(){
Collections.sort(emps, (e1, e2) -> {
if(e1.getAge() == e2.getAge()){
return e1.getName().compareTo(e2.getName());
}else{
return -Integer.compare(e1.getAge(), e2.getAge());
}
});
for (Employee emp : emps) {
System.out.println(emp);
}
}
@Test
public void test2(){
String trimStr = strHandler("\t\t\t 我大***威武 ", (str) -> str.trim());
System.out.println(trimStr);
String upper = strHandler("abcdef", (str) -> str.toUpperCase());
System.out.println(upper);
String newStr = strHandler("我大***威武", (str) -> str.substring(2, 5));
System.out.println(newStr);
}
//需求:用于处理字符串
public String strHandler(String str, MyFunction mf){
return mf.getValue(str);
}
@Test
public void test3(){
op(100L, 200L, (x, y) -> x + y);
op(100L, 200L, (x, y) -> x * y);
}
//需求:对于两个 Long 型数据进行处理
public void op(Long l1, Long l2, MyFunction2 mf){
System.out.println(mf.getValue(l1, l2));
}
}
只包含一个抽象方法的接口,称为函数式接口。
你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
我们可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
在java.util.function包下定义了java 8 的丰富的函数式接口
Java从诞生日起就是一直倡导“一切皆对象”,在java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF(面向函数编程)
在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
所以以前用匿名内部类表示的现在都可以用Lambda表达式来写。
函数式接口中使用泛型:
作为参数传递 Lambda 表达式:
作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。
函数式接口 |
参数类型 |
返回类型 |
用途 |
Consumer 消费型接口 |
T |
void |
对类型为T的对象应用操作,包含方法: void accept(T t) |
Supplier 供给型接口 |
无 |
T |
返回类型为T的对象,包含方法:T get() |
Function 函数型接口 |
T |
R |
对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t) |
Predicate 断定型接口 |
T |
boolean |
确定类型为T的对象是否满足某约束,并返回 boolean 值。包含方法 boolean test(T t) |
函数式接口 |
参数类型 |
返回类型 |
用途 |
BiFunction |
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 |
void |
对类型为T, U 参数应用操作。包含方法为 void accept(T t, U u) |
BiPredicate |
T,U |
boolean |
包含方法为 boolean test(T t,U u) |
ToIntFunction ToLongFunction ToDoubleFunction |
T |
int long double |
分别计算int、long、double、值的函数 |
IntFunction LongFunction DoubleFunction |
int long double |
R |
参数分别为int、long、double 类型的函数 |
创建一个函数式接口:MyFun
@FunctionalInterface
public interface MyFun {
public Integer getValue(Integer num);
}
import org.junit.Test;
/*
* 二、Lambda 表达式需要“函数式接口”的支持
* 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。 可以使用注解 @FunctionalInterface 修饰
* 可以检查是否是函数式接口
*/
public class TestLambda2 {
//需求:对一个数进行运算
@Test
public void test6(){
Integer num = operation(100, (x) -> x * x);
System.out.println(num);
System.out.println(operation(200, (y) -> y + 200));
}
public Integer operation(Integer num, MyFun mf){
return mf.getValue(num);
}
}
可以这么说,函数式接口中只能有一个抽象方法,可以有实现的静态方法;
@FunctionalInterface这个只是一个编译级的注解,用来检测你编写的函数式接口是否有错的
@FunctionalInterface
interface TestInterface {
public final static String value ="111";
static void staticFuc(){
}
void Test();
}
一、功能性接口:Function 接收一个功能参数t,并返回一个功能结果R。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function {
R apply(T t);
default Function compose(Function super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default Function andThen(Function super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static Function identity() {
return t -> t;
}
}
举例:可以简写为 Function
Function function = (str) ->{
return "hello,"+str;
};
@Test
public void test() {
String str = function.apply("Tassel");
System.out.println(str);
}
二、断言性接口:Predicate 主要用到test方法 其中参数t为输入参数,如果输入参数符合断言则返回true,否则false
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate {
boolean test(T t);
default Predicate and(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate negate() {
return (t) -> !test(t);
}
default Predicate or(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static Predicate isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
举例子:下面的等价于 "Hello".equalsIgnoreCase(t) //t:输入参数
@Test
public void test2() {
Predicate predicate = "Hello"::equalsIgnoreCase;
System.out.println(predicate.test("Hello"));
}
三、供给性接口:Supplier 不接收任何参数 但有返回值
@FunctionalInterface
public interface Supplier {
T get();
}
@Test
public void test3() {
Supplier supplier = "Hello"::toLowerCase;
System.out.println(supplier.get());
}
四、消费性接口:Consumer 只接收一个参数t,但是没有返回值。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer {
void accept(T t);
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Consumer consumer = System.out ::println;
consumer.accept("1111");
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;
import org.junit.Test;
/*
* Java8 内置的四大核心函数式接口
*
* Consumer : 消费型接口
* void accept(T t);
*
* Supplier : 供给型接口
* T get();
*
* Function : 函数型接口
* R apply(T t);
*
* Predicate : 断言型接口
* boolean test(T t);
*
*/
public class TestLambda3 {
//Predicate 断言型接口:
@Test
public void test4(){
List list = Arrays.asList("Hello", "at**", "Lambda", "www", "ok");
List strList = filterStr(list, (s) -> s.length() > 3);
for (String str : strList) {
System.out.println(str);
}
}
//需求:将满足条件的字符串,放入集合中
public List filterStr(List list, Predicate pre){
List strList = new ArrayList<>();
for (String str : list) {
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
//Function 函数型接口:
@Test
public void test3(){
String newStr = strHandler("\t\t\t 我大***威武 ", (str) -> str.trim());
System.out.println(newStr);
String subStr = strHandler("我大***威武", (str) -> str.substring(2, 5));
System.out.println(subStr);
}
//需求:用于处理字符串
public String strHandler(String str, Function fun){
return fun.apply(str);
}
//Supplier 供给型接口 :
@Test
public void test2(){
List numList = getNumList(10, () -> (int)(Math.random() * 100));
for (Integer num : numList) {
System.out.println(num);
}
}
//需求:产生指定个数的整数,并放入集合中
public List getNumList(int num, Supplier sup){
List list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
return list;
}
//Consumer 消费型接口 :
@Test
public void test1(){
happy(10000, (m) -> System.out.println("你们刚哥喜欢大宝剑,每次消费:" + m + "元"));
}
public void happy(double money, Consumer con){
con.accept(money);
}
}
1、当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
2、方法引用就是Lambda表达式,就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
3、要求:实现抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
4、方法引用:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。
5、如下三种主要使用情况:
对象::实例方法
类::静态方法
类::实例方法
例如:
等同于:
例如:
等同于:
例如:
等同于:
注意:当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时:ClassName::methodName
例如
(x) -> System.out.println(x);
等同于
System.out::println
例如
BinaryOperator bo = (x,y)->Math.pow(x, y);
等同于
BinaryOperator bo = Math::pow;
例如
compare((x,y) -> x.equals(y),"abcdef","abcdef");
等同于
compare(String::equals,"abcdef","abcdef");
@Test
public void test1(){
PrintStream ps = System.out;
Consumer con = (str) -> ps.println(str);
con.accept("Hello World!");
System.out.println("--------------------------------");
Consumer con2 = ps::println;
con2.accept("Hello Java8!");
Consumer con3 = System.out::println;
con3.accept("Hello Java9!");
}
一、对象::实例方法
方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!
//对象的引用 :: 实例方法名
@Test
public void test2(){
Employee emp = new Employee(101, "张三", 18, 9999.99);
Supplier sup = () -> emp.getName();
System.out.println(sup.get());
System.out.println("----------------------------------");
Supplier sup2 = emp::getName;
System.out.println(sup2.get());
}
二、类::静态方法
//类名 :: 静态方法名
@Test
public void test4(){
Comparator com = (x, y) -> Integer.compare(x, y);
System.out.println("-------------------------------------");
Comparator com2 = Integer::compare;
}
@Test
public void test3(){
BiFunction fun = (x, y) -> Math.max(x, y);
System.out.println(fun.apply(1.5, 22.2));
System.out.println("--------------------------------------------------");
BiFunction fun2 = Math::max;
System.out.println(fun2.apply(1.2, 1.5));
}
三、类::实例方法
若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName
//类名 :: 实例方法名
@Test
public void test5(){
BiPredicate bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abcde", "abcde"));
System.out.println("-----------------------------------------");
BiPredicate bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));
System.out.println("-----------------------------------------");
Function fun = (e) -> e.show();
System.out.println(fun.apply(new Employee()));
System.out.println("-----------------------------------------");
Function fun2 = Employee::show;
System.out.println(fun2.apply(new Employee()));
}
格式: ClassName::new
与函数式接口相结合,自动与函数式接口中方法兼容。
可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
//构造器引用
/**
* 到底调用那个构造器,取决于函数式接口中抽象方法的参数列表,两则保存一致
*/
@Test
public void test7(){
Function fun = Employee::new;
Employee emp = fun.apply("张三");
System.out.println(emp);
BiFunction fun2 = Employee::new;
Employee emp2 = fun2.apply("李四",18);
}
@Test
public void test6(){
Supplier sup = () -> new Employee();
System.out.println(sup.get());
System.out.println("------------------------------------");
Supplier sup2 = Employee::new;
System.out.println(sup2.get());
}
//数组引用
@Test
public void test8(){
Function fun = (args) -> new String[args];
String[] strs = fun.apply(10);
System.out.println(strs.length);
System.out.println("--------------------------");
Function fun2 = Employee[] :: new;
Employee[] emps = fun2.apply(20);
System.out.println(emps.length);
}
全部代码:
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.junit.Test;
/*
* 一、方法引用:若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用
* (可以将方法引用理解为 Lambda 表达式的另外一种表现形式)
*
* 1. 对象的引用 :: 实例方法名
*
* 2. 类名 :: 静态方法名
*
* 3. 类名 :: 实例方法名
*
* 注意:
* ①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!
* ②若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName
*
* 二、构造器引用 :构造器的参数列表,需要与函数式接口中参数列表保持一致!
*
* 1. 类名 :: new
*
* 三、数组引用
*
* 类型[] :: new;
*
*
*/
public class TestMethodRef {
//数组引用
@Test
public void test8(){
Function fun = (args) -> new String[args];
String[] strs = fun.apply(10);
System.out.println(strs.length);
System.out.println("--------------------------");
Function fun2 = Employee[] :: new;
Employee[] emps = fun2.apply(20);
System.out.println(emps.length);
}
//构造器引用
@Test
public void test7(){
Function fun = Employee::new;
BiFunction fun2 = Employee::new;
}
@Test
public void test6(){
Supplier sup = () -> new Employee();
System.out.println(sup.get());
System.out.println("------------------------------------");
Supplier sup2 = Employee::new;
System.out.println(sup2.get());
}
//类名 :: 实例方法名
@Test
public void test5(){
BiPredicate bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abcde", "abcde"));
System.out.println("-----------------------------------------");
BiPredicate bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));
System.out.println("-----------------------------------------");
Function fun = (e) -> e.show();
System.out.println(fun.apply(new Employee()));
System.out.println("-----------------------------------------");
Function fun2 = Employee::show;
System.out.println(fun2.apply(new Employee()));
}
//类名 :: 静态方法名
@Test
public void test4(){
Comparator com = (x, y) -> Integer.compare(x, y);
System.out.println("-------------------------------------");
Comparator com2 = Integer::compare;
}
@Test
public void test3(){
BiFunction fun = (x, y) -> Math.max(x, y);
System.out.println(fun.apply(1.5, 22.2));
System.out.println("--------------------------------------------------");
BiFunction fun2 = Math::max;
System.out.println(fun2.apply(1.2, 1.5));
}
//对象的引用 :: 实例方法名
@Test
public void test2(){
Employee emp = new Employee(101, "张三", 18, 9999.99);
Supplier sup = () -> emp.getName();
System.out.println(sup.get());
System.out.println("----------------------------------");
Supplier sup2 = emp::getName;
System.out.println(sup2.get());
}
@Test
public void test1(){
PrintStream ps = System.out;
Consumer con = (str) -> ps.println(str);
con.accept("Hello World!");
System.out.println("--------------------------------");
Consumer con2 = ps::println;
con2.accept("Hello Java8!");
Consumer con3 = System.out::println;
}
}
原理图:
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要java层面去处理。
流(Stream) 到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
基础接口BaseStream
public interface BaseStream> extends AutoCloseable {
Iterator iterator();
Spliterator spliterator();
boolean isParallel();//判断是否是并行化流
S sequential();//将流串行化
S parallel(); //将流并行化
S unordered(); //解除有序流的顺序限制,发挥并行处理的性能优势
S onClose(Runnable closeHandler);
@Override
void close();
}
默认情况下,从有序集合、生成器、迭代器产生的流或者通过调用Stream.sorted产生的流都是有序流,有序流在并行处理时会在处理完成之后恢复原顺序。unordered()方法可以解除有序流的顺序限制,更好地发挥并行处理的性能优势,例如distinct将保存任意一个唯一元素而不是第一个,limit将保留任意n个元素而不是前n个。
BaseStream的四个子接口方法都差不多,只是IntStream、LongStream、DoubleStream直接存储基本类型,可以避免自动装/拆箱,效率会更高一些。
1- 创建 Stream
一个数据源(如:集合、数组),获取一个流
2- 中间操作
一个中间操作链,对数据源的数据进行处理
3- 终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
Java8 中的 Collection 接口被扩展,提供了 两个获取流的方法:
default Stream stream() : 返回一个顺序流
default Stream parallelStream() : 返回一个并行流
Java8 中的 Arrays 的静态方法 stream() 可 以获取数组流:
static Stream stream(T[] array): 返回一个流
重载形式,能够处理对应基本类型的数组:
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
可以使用静态方法 Stream.of(), 通过显示值 创建一个流。它可以接收任意数量的参数。
public static Stream of(T... values) : 返回一个流
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
迭代
public static Stream iterate(final T seed, final UnaryOperator f)
生成
public static Stream generate(Supplier s)
//1. 创建 Stream
@Test
public void test1(){
//1. Collection 提供了两个方法 stream() 与 parallelStream()
List list = new ArrayList<>();
Stream stream = list.stream(); //获取一个顺序流
Stream parallelStream = list.parallelStream(); //获取一个并行流
//2. 通过 Arrays 中的 stream() 获取一个数组流
Integer[] nums = new Integer[10];
Stream stream1 = Arrays.stream(nums);
//3. 通过 Stream 类中静态方法 of()
Stream stream2 = Stream.of(1,2,3,4,5,6);
//4. 创建无限流
//迭代
Stream stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
stream3.forEach(System.out::println);
//生成
Stream stream4 = Stream.generate(Math::random).limit(2);
stream4.forEach(System.out::println);
}
多个中间操作可以连接起来形成一个流水线,除非流水 线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值”。
方 法 |
描 述 |
filter(Predicate p) |
接收 Lambda , 从流中排除某些元素 |
distinct() |
筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
limit(long maxSize) |
截断流,使其元素不超过给定数量 |
skip(long n) |
跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
//2. 中间操作
List 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——接收 Lambda , 从流中排除某些元素。
limit——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
*/
//内部迭代:迭代操作 Stream API 内部完成
@Test
public void test2(){
//所有的中间操作不会做任何的处理
Stream stream = emps.stream()
.filter((e) -> {
System.out.println("测试中间操作");
return e.getAge() <= 35;
});
//只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
stream.forEach(System.out::println);
}
//外部迭代
@Test
public void test3(){
Iterator it = emps.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
测试中间操作
测试中间操作
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=104, name=赵六, age=8, salary=7777.77]
测试中间操作
Employee [id=104, name=赵六, age=8, salary=7777.77]
测试中间操作
@Test
public void test4(){
emps.stream()
.filter((e) -> {
System.out.println("短路!"); // && ||
return e.getSalary() >= 5000;
}).limit(3)//找到了 3 条 后面的就不在去找了
.forEach(System.out::println);
}
短路!
Employee [id=102, name=李四, age=59, salary=6666.66]
短路!
Employee [id=101, name=张三, age=18, salary=9999.99]
短路!
短路!
Employee [id=104, name=赵六, age=8, salary=7777.77]
@Test
public void test5(){
emps.parallelStream()
.filter((e) -> e.getSalary() >= 5000)
.skip(2)
.forEach(System.out::println);
}
//需要重写 Employee 的hashCode() 和 equals()
@Test
public void test6(){
emps.stream()
.distinct()
.forEach(System.out::println);
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
/*
* 一、Stream API 的操作步骤:
*
* 1. 创建 Stream
*
* 2. 中间操作
*
* 3. 终止操作(终端操作)
*/
public class TestStreamaAPI {
//1. 创建 Stream
@Test
public void test1(){
//1. Collection 提供了两个方法 stream() 与 parallelStream()
List list = new ArrayList<>();
Stream stream = list.stream(); //获取一个顺序流
Stream parallelStream = list.parallelStream(); //获取一个并行流
//2. 通过 Arrays 中的 stream() 获取一个数组流
Integer[] nums = new Integer[10];
Stream stream1 = Arrays.stream(nums);
//3. 通过 Stream 类中静态方法 of()
Stream stream2 = Stream.of(1,2,3,4,5,6);
//4. 创建无限流
//迭代
Stream stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
stream3.forEach(System.out::println);
//生成
Stream stream4 = Stream.generate(Math::random).limit(2);
stream4.forEach(System.out::println);
}
//2. 中间操作
List 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——接收 Lambda , 从流中排除某些元素。
limit——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
*/
//内部迭代:迭代操作 Stream API 内部完成
@Test
public void test2(){
//所有的中间操作不会做任何的处理
Stream stream = emps.stream()
.filter((e) -> {
System.out.println("测试中间操作");
return e.getAge() <= 35;
});
//只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
stream.forEach(System.out::println);
}
//外部迭代
@Test
public void test3(){
Iterator it = emps.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
@Test
public void test4(){
emps.stream()
.filter((e) -> {
System.out.println("短路!"); // && ||
return e.getSalary() >= 5000;
}).limit(3)
.forEach(System.out::println);
}
@Test
public void test5(){
emps.parallelStream()
.filter((e) -> e.getSalary() >= 5000)
.skip(2)
.forEach(System.out::println);
}
@Test
public void test6(){
emps.stream()
.distinct()
.forEach(System.out::println);
}
}
方法 |
描述 |
map(Function f) |
接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
mapToDouble(ToDoubleFunction f) |
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
mapToInt(ToIntFunction f) |
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
mapToLong(ToLongFunction f) |
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
flatMap(Function f) |
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
//2. 中间操作
/*
映射
map——接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
理解为 Collection 的 add 和 addAll 的关系
*/
@Test
public void test1(){
Stream str = emps.stream()
.map((e) -> e.getName());
System.out.println("-------------------------------------------");
List strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
Stream stream = strList.stream()
.map(String::toUpperCase);
stream.forEach(System.out::println);
Stream> stream2 = strList.stream()
.map(TestStreamAPI1::filterCharacter);
stream2.forEach((sm) -> {
sm.forEach(System.out::println);
});
System.out.println("---------------------------------------------");
Stream stream3 = strList.stream()
.flatMap(TestStreamAPI1::filterCharacter);
stream3.forEach(System.out::println);
}
public static Stream filterCharacter(String str){
List list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
方法 |
描述 |
sorted() |
产生一个新流,其中按自然顺序排序 |
sorted(Comparator com) |
产生一个新流,其中按比较器顺序排序 |
/*
sorted()——自然排序 sorted(Comparable)
sorted(Comparator com)——定制排序
*/
@Test
public void test2(){
emps.stream()
.map(Employee::getName)
.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);
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
/*
* 一、 Stream 的操作步骤
*
* 1. 创建 Stream
*
* 2. 中间操作
*
* 3. 终止操作
*/
public class TestStreamAPI1 {
List 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)
);
//2. 中间操作
/*
映射
map——接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
*/
@Test
public void test1(){
Stream str = emps.stream()
.map((e) -> e.getName());
System.out.println("-------------------------------------------");
List strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
Stream stream = strList.stream()
.map(String::toUpperCase);
stream.forEach(System.out::println);
Stream> stream2 = strList.stream()
.map(TestStreamAPI1::filterCharacter);
stream2.forEach((sm) -> {
sm.forEach(System.out::println);
});
System.out.println("---------------------------------------------");
Stream stream3 = strList.stream()
.flatMap(TestStreamAPI1::filterCharacter);
stream3.forEach(System.out::println);
}
public static Stream filterCharacter(String str){
List list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
/*
sorted()——自然排序
sorted(Comparator com)——定制排序
*/
@Test
public void test2(){
emps.stream()
.map(Employee::getName)
.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 。
流进行了终止操作后,不能再次使用。
方法 |
描述 |
allMatch(Predicate p) |
检查是否匹配所有元素 |
anyMatch(Predicate p) |
检查是否至少匹配一个元素 |
noneMatch(Predicate p) |
检查是否没有匹配所有元素 |
findFirst() |
返回第一个元素 |
findAny() |
返回当前流中的任意元素 |
方法 |
描述 |
count() |
返回流中元素总数 |
max(Comparator c) |
返回流中最大值 |
min(Comparator c) |
返回流中最小值 |
forEach(Consumer c) |
内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了) |
Employee 类中增加一个枚举内部类,重写其toString() 方法
public class Employee {
private int id;
private String name;
private int age;
private double salary;
private Status status;
public Employee() {
}
public Employee(String name) {
this.name = name;
}
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 Employee(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;
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 + ", status=" + status
+ "]";
}
public enum Status {
FREE, BUSY, VOCATION;
}
}
List emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
//3. 终止操作
/*
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
*/
@Test
public void test1(){
boolean bl = emps.stream()
.allMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl);
boolean bl1 = emps.stream()
.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl1);
boolean bl2 = emps.stream()
.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl2);
}
@Test
public void test2(){
Optional op = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
//op.orElse(emps.get(1)); 为了防止空指针异常返回的是一个Optional 如果为空就就用元素替代
System.out.println(op.get());
System.out.println("--------------------------------");
Optional op2 = emps.parallelStream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.findAny();
System.out.println(op2.get());
}
@Test
public void test3(){
long count = emps.stream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.count();
System.out.println(count);
Optional op = emps.stream()
.map(Employee::getSalary)
.max(Double::compare);
System.out.println(op.get());
Optional op2 = emps.stream()
.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op2.get());
}
//注意:流进行了终止操作后,不能再次使用
@Test
public void test4(){
Stream stream = emps.stream()
.filter((e) -> e.getStatus().equals(Status.FREE));
long count = stream.count();
stream.map(Employee::getSalary)
.max(Double::compare);
}
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.junit.Test;
import com.atguigu.java8.Employee.Status;
/*
* 一、 Stream 的操作步骤
*
* 1. 创建 Stream
*
* 2. 中间操作
*
* 3. 终止操作
*/
public class TestStreamAPI2 {
List emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
//3. 终止操作
/*
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
*/
@Test
public void test1(){
boolean bl = emps.stream()
.allMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl);
boolean bl1 = emps.stream()
.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl1);
boolean bl2 = emps.stream()
.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl2);
}
@Test
public void test2(){
Optional op = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(op.get());
System.out.println("--------------------------------");
Optional op2 = emps.parallelStream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.findAny();
System.out.println(op2.get());
}
@Test
public void test3(){
long count = emps.stream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.count();
System.out.println(count);
Optional op = emps.stream()
.map(Employee::getSalary)
.max(Double::compare);
System.out.println(op.get());
Optional op2 = emps.stream()
.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op2.get());
}
//注意:流进行了终止操作后,不能再次使用
@Test
public void test4(){
Stream stream = emps.stream()
.filter((e) -> e.getStatus().equals(Status.FREE));
long count = stream.count();
stream.map(Employee::getSalary)
.max(Double::compare);
}
}
方法 |
描述 |
reduce(T iden, BinaryOperator b) |
可以将流中元素反复结合起来,得到一个值。返回 T |
reduce(BinaryOperator b) |
可以将流中元素反复结合起来,得到一个值。返回 Optional |
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它 来进行网络搜索而出名。
//3. 终止操作
/*
归约
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。
*/
@Test
public void test1(){
List list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
//求和 首先把0(起始值)作为x,从流中取出1作为y x+y = 1,然后把1作为x,从流中取出2作为y x+y = 3 循环往复
System.out.println(sum);
System.out.println("----------------------------------------");
Optional op = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(op.get());
//第一个返回的是Integer ,应为无论如何都有个起始值,不可能为空,而下面这个是有可能为空的
}
方 法 |
描 述 |
collect(Collector c) |
将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 |
Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。
另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
方法 |
返回类型 |
作用 |
toList |
List |
把流中元素收集到List |
List |
||
toSet |
Set |
把流中元素收集到Set |
Set |
||
toCollection |
Collection |
把流中元素收集到创建的集合 |
Collection |
||
counting |
Long |
计算流中元素的个数 |
long count = list.stream().collect(Collectors.counting()); |
||
summingInt |
Integer |
对流中元素的整数属性求和 |
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary)); |
||
averagingInt |
Double |
计算流中元素Integer属性的平均值 |
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary)); |
||
summarizingInt |
IntSummaryStatistics |
收集流中Integer属性的统计值。如:平均值 |
int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); |
joining |
String |
连接流中每个字符串 |
String str= list.stream().map(Employee::getName).collect(Collectors.joining()); |
||
maxBy |
Optional |
根据比较器选择最大值 |
Optional |
||
minBy |
Optional |
根据比较器选择最小值 |
Optional |
||
reducing |
归约产生的类型 |
从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值 |
int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum)); |
||
collectingAndThen |
转换函数返回的类型 |
包裹另一个收集器,对其结果转换函数 |
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); |
||
groupingBy |
Map |
根据某属性值对流分组,属性为K,结果为V |
Map |
||
partitioningBy |
Map |
根据true或false进行分区 |
Map |
//collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
@Test
public void test3(){
List list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("----------------------------------");
Set set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("----------------------------------");
//搜集到特殊的集合中
HashSet hs = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hs.forEach(System.out::println);
}
@Test
public void test4(){
Optional max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
Optional op = emps.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
System.out.println("--------------------------------------------");
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
}
//分组
//按照状态分组
@Test
public void test5(){
Map> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
}
//多级分组
@Test
public void test6(){
Map>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if(e.getAge() >= 60)
return "老年";
else if(e.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(map);
}
//分区
//分成 true 和 false 两个区
@Test
public void test7(){
Map> map = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
System.out.println(map);
}
//连接
@Test
public void test10(){
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("," , "----", "----"));
System.out.println(str);
}
@Test
public void test11(){
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getSum());
System.out.println(dss.getAverage());
System.out.println(dss.getMax());
}
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Test;
import com.atguigu.java8.Employee.Status;
public class TestStreamAPI3 {
List emps = Arrays.asList(
new Employee(102, "李四", 79, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
//3. 终止操作
/*
归约
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。
*/
@Test
public void test1(){
List list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
System.out.println("----------------------------------------");
Optional op = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(op.get());
}
//需求:搜索名字中 “六” 出现的次数
@Test
public void test2(){
Optional sum = emps.stream()
.map(Employee::getName)
.flatMap(TestStreamAPI1::filterCharacter)
.map((ch) -> {
if(ch.equals('六'))
return 1;
else
return 0;
}).reduce(Integer::sum);
System.out.println(sum.get());
}
//collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
@Test
public void test3(){
List list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("----------------------------------");
Set set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("----------------------------------");
HashSet hs = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hs.forEach(System.out::println);
}
@Test
public void test4(){
Optional max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
Optional op = emps.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
System.out.println("--------------------------------------------");
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
}
//分组
@Test
public void test5(){
Map> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
}
//多级分组
@Test
public void test6(){
Map>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if(e.getAge() >= 60)
return "老年";
else if(e.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(map);
}
//分区
@Test
public void test7(){
Map> map = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
System.out.println(map);
}
//
@Test
public void test8(){
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("," , "----", "----"));
System.out.println(str);
}
@Test
public void test9(){
Optional sum = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.reducing(Double::sum));
System.out.println(sum.get());
}
}
练习:
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.junit.Test;
import com.atguigu.java8.Employee;
import com.atguigu.java8.Employee.Status;
public class TestStreamAPI {
/*
1. 给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?
,给定【1,2,3,4,5】, 应该返回【1,4,9,16,25】。
*/
@Test
public void test1(){
Integer[] nums = new Integer[]{1,2,3,4,5};
Arrays.stream(nums)
.map((x) -> x * x)
.forEach(System.out::println);
}
/*
2. 怎样用 map 和 reduce 方法数一数流中有多少个Employee呢?
*/
List emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
@Test
public void test2(){
Optional count = emps.stream()
.map((e) -> 1)
.reduce(Integer::sum);
System.out.println(count.get());
}
}
//交易员类
public class Trader {
private String name;//交易员名字
private String city;//交易员城市
public Trader() {
}
public Trader(String name, String city) {
this.name = name;
this.city = city;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Trader [name=" + name + ", city=" + city + "]";
}
}
//交易类
public class Transaction {
private Trader trader;//交易员
private int year;//交易年份
private int value;//交易额
public Transaction() {
}
public Transaction(Trader trader, int year, int value) {
this.trader = trader;
this.year = year;
this.value = value;
}
public Trader getTrader() {
return trader;
}
public void setTrader(Trader trader) {
this.trader = trader;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
@Override
public String toString() {
return "Transaction [trader=" + trader + ", year=" + year + ", value="
+ value + "]";
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Test;
public class TestTransaction {
List transactions = null;
@Before
public void before(){
Trader raoul = new Trader("Raoul", "Cambridge");
Trader mario = new Trader("Mario", "Milan");
Trader alan = new Trader("Alan", "Cambridge");
Trader brian = new Trader("Brian", "Cambridge");
transactions = Arrays.asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950)
);
}
//1. 找出2011年发生的所有交易, 并按交易额排序(从低到高)
@Test
public void test1(){
transactions.stream()
.filter((t) -> t.getYear() == 2011)
.sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
.forEach(System.out::println);
}
//2. 交易员都在哪些不同的城市工作过?
@Test
public void test2(){
transactions.stream()
.map((t) -> t.getTrader().getCity())
.distinct()
.forEach(System.out::println);
}
//3. 查找所有来自剑桥的交易员,并按姓名排序
@Test
public void test3(){
transactions.stream()
.filter((t) -> t.getTrader().getCity().equals("Cambridge"))
.map(Transaction::getTrader)
.sorted((t1, t2) -> t1.getName().compareTo(t2.getName()))
.distinct()
.forEach(System.out::println);
}
//4. 返回所有交易员的姓名字符串,按字母顺序排序
@Test
public void test4(){
transactions.stream()
.map((t) -> t.getTrader().getName())
.sorted()
.forEach(System.out::println);
System.out.println("-----------------------------------");
String str = transactions.stream()
.map((t) -> t.getTrader().getName())
.sorted()
.reduce("", String::concat);
System.out.println(str);
System.out.println("------------------------------------");
transactions.stream()
.map((t) -> t.getTrader().getName())
.flatMap(TestTransaction::filterCharacter)
.sorted((s1, s2) -> s1.compareToIgnoreCase(s2))
.forEach(System.out::print);
}
public static Stream filterCharacter(String str){
List list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch.toString());
}
return list.stream();
}
//5. 有没有交易员是在米兰工作的?
@Test
public void test5(){
boolean bl = transactions.stream()
.anyMatch((t) -> t.getTrader().getCity().equals("Milan"));
System.out.println(bl);
}
//6. 打印生活在剑桥的交易员的所有交易额
@Test
public void test6(){
Optional sum = transactions.stream()
.filter((e) -> e.getTrader().getCity().equals("Cambridge"))
.map(Transaction::getValue)
.reduce(Integer::sum);
System.out.println(sum.get());
}
//7. 所有交易中,最高的交易额是多少
@Test
public void test7(){
Optional max = transactions.stream()
.map((t) -> t.getValue())
.max(Integer::compare);
System.out.println(max.get());
}
//8. 找到交易额最小的交易
@Test
public void test8(){
Optional op = transactions.stream()
.min((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()));
System.out.println(op.get());
}
}
补充:
最后提一下基本类型流(IntStream、LongStream.....),与对象流(Stream)的不同点如下:
- IntStream和LongStream有range(start, end)和rangeClosed(start, end)方法,可以生成步长为1的整数范围,前者不包括end,后者包括end
- toArray方法将返回基本类型数组
- 具有sum、average、max、min方法
- summaryStatics()方法会产生类型为Int/Long/DoubleSummaryStatistics的对象
- 可以使用Random类的ints、longs、doubles方法产生随机数构成的流
- 对象流转换为基本类型流:mapToInt()、mapToLong()、mapToDouble()
- 基本类型流转换为对象流:boxed()
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。
并行流就是把一个内容(数组或集合)分成多个数据块,并用不同的线程分别处理每个数据块的流。这样一来,你就可以自动把给定操作的工作负荷分配给多核处理器的所有内核,让他们都忙起来。整个过程无需程序员显示实现优化。
public static long parallelSum(long n){
return Stream.iterate(1L,i -> i +1) .limit(n) .parallel()
.reduce(0L,Long::sum);
}
Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。
采用 “工作窃取”模式(work-stealing):
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。
相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上。在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态。而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行。这种方式减少了线程的等待时间,提高了性能。
import java.util.concurrent.RecursiveTask;
public class ForkJoinCalculate extends RecursiveTask{//Recursive 递归的
/**
*
*/
private static final long serialVersionUID = 13475679780L;
private long start;
private long end;
private static final long THRESHOLD = 10000L; //临界值 最小的任务
public ForkJoinCalculate(long start, long end) {
this.start = start;
this.end = end;
}
@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(middle+1, end);
right.fork();
return left.join() + right.join();//合并 累加总和
}
}
}
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
import org.junit.Test;
public class TestForkJoin {
//ForkJoin 框架
@Test
public void test1(){
long start = System.currentTimeMillis();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask task = new ForkJoinCalculate(0L, 10000000000L);
long sum = pool.invoke(task);
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("耗费的时间为: " + (end - start)); //112-1953-1988-2654-2647-20663-113808
}
//普通 for循环
@Test
public void test2(){
long start = System.currentTimeMillis();
long sum = 0L;
for (long i = 0L; i <= 10000000000L; i++) {
sum += i;
}
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("耗费的时间为: " + (end - start)); //34-3174-3132-4227-4223-31583
}
//java8 要完成上面的操作要简单的多了 底层还是ForkJoin 框架
@Test
public void test3(){
long start = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(0L, 10000000000L)
.parallel()
.sum();
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("耗费的时间为: " + (end - start)); //2061-2053-2086-18926
}
}
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional
常用方法:
Optional.empty() : 创建一个空的 Optional 实例
Optional.of(T t) : 创建一个 Optional 实例
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
isPresent() : 判断是否包含值
T get(): 如果调用对象包含值,返回该值,否则抛异常
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
Optional.of(T t) : 创建一个 Optional 实例
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
@Test
public void test1(){
Optional op = Optional.of(new Employee());
Employee emp = op.get();
System.out.println(emp);
}
@Test
public void test2(){
//Optional op = Optional.of(null);//这样是不可以的,这样可以 以最快的速度定位异常
//有的时候我就想构建一个空的Optional的
Optional op3 = Optional.empty();
System.out.println(op3.get());
Optional op2 = Optional.ofNullable(null);
System.out.println(op2.get());
}
//ofNullable 是另外两个的综合
//public static Optional ofNullable(T value) {
// return value == null ? empty() : of(value);
//}
isPresent() : 判断是否包含值
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
@Test
public void test3(){
Optional op = Optional.ofNullable(new Employee());
if(op.isPresent()){
System.out.println(op.get());
}
Employee emp = op.orElse(new Employee("张三"));
System.out.println(emp);
//和上面的相比,这个可以添加判断
Employee emp2 = op.orElseGet(() -> new Employee());
System.out.println(emp2);
}
map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
@Test
public void test4(){
Optional op = Optional.of(new Employee(101, "张三", 18, 9999.99));
Optional op2 = op.map(Employee::getName);
System.out.println(op2.get());
//进一步避免异常
Optional op3 = op.flatMap((e) -> Optional.of(e.getName()));
System.out.println(op3.get());
}
练习:
女神类:
public class Godness {
private String name;
public Godness() {
}
public Godness(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Godness [name=" + name + "]";
}
}
男人类:
public class Man {
private Godness god;
public Man() {
}
public Man(Godness god) {
this.god = god;
}
public Godness getGod() {
return god;
}
public void setGod(Godness god) {
this.god = god;
}
@Override
public String toString() {
return "Man [god=" + god + "]";
}
}
男人不一定有女神
@Test
public void test5(){
Man man = new Man();
String name = getGodnessName(man);
System.out.println(name);
}
//需求:获取一个男人心中女神的名字
public String getGodnessName(Man man){
if(man != null){
Godness g = man.getGod();
if(g != null){
return g.getName();
}
}
return "苍老师";//默认值
}
运用 Optional 的实体类,封装 Godness (包装有可能没有的值)
import java.util.Optional;
//注意:Optional 不能被序列化
public class NewMan {
//可能为空 我就把它包装一下
private Optional godness = Optional.empty();
private Godness god;
public Optional getGod(){
return Optional.of(god);
}
public NewMan() {
}
public NewMan(Optional godness) {
this.godness = godness;
}
public Optional getGodness() {
return godness;
}
public void setGodness(Optional godness) {
this.godness = godness;
}
@Override
public String toString() {
return "NewMan [godness=" + godness + "]";
}
}
//运用 Optional 的实体类
@Test
public void test6(){
//Optional godness = Optional.ofNullable(null); // 返回默认值苍老师
Optional godness = Optional.ofNullable(new Godness("林志玲"));
Optional op = Optional.ofNullable(new NewMan(godness));
String name = getGodnessName2(op);
System.out.println(name);
}
public String getGodnessName2(Optional man){
return man.orElse(new NewMan())
.getGodness()
.orElse(new Godness("苍老师"))
.getName();
}
import java.util.Optional;
import org.junit.Test;
/*
* 一、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
*/
public class TestOptional {
@Test
public void test4(){
Optional op = Optional.of(new Employee(101, "张三", 18, 9999.99));
Optional op2 = op.map(Employee::getName);
System.out.println(op2.get());
Optional op3 = op.flatMap((e) -> Optional.of(e.getName()));
System.out.println(op3.get());
}
@Test
public void test3(){
Optional op = Optional.ofNullable(new Employee());
if(op.isPresent()){
System.out.println(op.get());
}
Employee emp = op.orElse(new Employee("张三"));
System.out.println(emp);
Employee emp2 = op.orElseGet(() -> new Employee());
System.out.println(emp2);
}
@Test
public void test2(){
Optional op = Optional.of(null);//这样是不可以的,
// Optional op = Optional.ofNullable(null);
// System.out.println(op.get());
// Optional op = Optional.empty();
// System.out.println(op.get());
}
@Test
public void test1(){
Optional op = Optional.of(new Employee());
Employee emp = op.get();
System.out.println(emp);
}
@Test
public void test5(){
Man man = new Man();
String name = getGodnessName(man);
System.out.println(name);
}
//需求:获取一个男人心中女神的名字
public String getGodnessName(Man man){
if(man != null){
Godness g = man.getGod();
if(g != null){
return g.getName();
}
}
return "苍老师";
}
//运用 Optional 的实体类
@Test
public void test6(){
Optional godness = Optional.ofNullable(new Godness("林志玲"));
Optional op = Optional.ofNullable(new NewMan(godness));
String name = getGodnessName2(op);
System.out.println(name);
}
public String getGodnessName2(Optional man){
return man.orElse(new NewMan())
.getGodness()
.orElse(new Godness("苍老师"))
.getName();
}
}
接口的新特性:Java 8中关于接口的改进
Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。
默认方法:Java 8中允许接口中包含具有具体实现的方法,该方法称为 “默认方法”,默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法。
这样设计的猜想:
比如 Collection 接口是所有集合的祖先,但是我们还有个 Collections 的工具类,这两个东西为什么不能放在同一个类中呢?还要专门在写一个类。
java8 的函数式接口由于只能有一个抽象方法,为了支持原来的。(java8 为了完全兼容 java7里面很多接口 )
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
>> 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
>> 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么实现类必须覆盖该方法来解决冲突。
public interface MyFun {
default String getName(){
return "哈哈哈";
}
}
public class MyClass {
public String getName(){
return "嘿嘿嘿";
}
}
SubClass 继承了 MyClass,实现 MyFun ,出现同名方法 getName
public class SubClass extends MyClass implements MyFun{
}
子类 TestDefaultInterface 去调用 getName 方法实际上是调用 继承的MyClass的getName 方法
public class TestDefaultInterface {
public static void main(String[] args) {
SubClass sc = new SubClass();
System.out.println(sc.getName());
}
}
嘿嘿嘿
public interface MyFun {
default String getName(){
return "哈哈哈";
}
}
public interface MyInterface {
default String getName() {
return "呵呵呵";
}
}
连个接口有同名的方法(默认方法)了,这个时候必须去实现其中一个方法
public class SubClass implements MyFun, MyInterface{
@Override
public String getName() {
return MyInterface.super.getName();
}
}
Java8 中,接口中允许添加静态方法
public interface MyInterface {
public static void show(){
System.out.println("接口中的静态方法");
}
}
public class TestDefaultInterface {
public static void main(String[] args) {
MyInterface.show();
}
}
时间戳:时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
@Test
/**
* java8 计算程序运行时间
*/
public void test10(){
Instant start = Instant.now();
//......程序.......
Instant end = Instant.now();
System.out.println("耗费时间为:"+ Duration.between(start,end).toMillis()+"毫秒");
}
如果我们可以跟别人说:“我们在1502643933071见面,别晚了!”那么就再简单不过了。但是我们希望时间与昼夜和四季有关,于是事情就变复杂了。JDK 1.0中包含了一个 java.util.Date 类,但是它的大多数方法已经在JDK 1.1引入Calendar类之后被弃用了。而Calendar并不比Date好多少。它们面临的问题是:
可变性:像日期和时间这样的类应该是不可变的。
偏移性:Date中的年份是从1900开始的,而月份都从0开始。
格式化:格式化只对Date有用,Calendar则不行。
此外,它们也不是线程安全的;不能处理闰秒等。
总结:对日期和时间的操作一直是Java程序员最痛苦的地方之一。
第三次引入的API是成功的,并且java 8中引入的java.time API 已经纠正了过去的缺陷,将来很长一段时间内它都会为我们服务。
Java 8 吸收了 Joda-Time 的精华,以一个新的开始为 Java 创建优秀的 API。新的 java.time 中包含了所有关于本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)的类。历史悠久的 Date 类新增了 toInstant() 方法,用于把 Date 转换成新的表示形式。这些新增的本地化时间日期 API 大大简化了日期时间和本地化的管理。
java.time – 包含值对象的基础包
java.time.chrono – 提供对不同的日历系统的访问
java.time.format – 格式化和解析时间和日期
java.time.temporal – 包括底层框架和扩展特性
java.time.zone – 包含时区支持的类
说明:大多数开发者只会用到基础包和format包,也可能会用到temporal包。因此,尽管有68个新的公开类型,大多数开发者,大概将只会用到其中的三分之一。
线程安全演示:
@Test
public void test() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable task = new Callable() {
@Override
public Date call() throws Exception {
return sdf.parse("20161121");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);//线程池
List> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future future : results) {
System.out.println(future.get());
}
pool.shutdown();
}
上面的程序会抛出异常,下面的采用线程安全来做。
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatThreadLocal {
private static final ThreadLocal df = new ThreadLocal(){
protected DateFormat initialValue(){
return new SimpleDateFormat("yyyyMMdd");
}
};
public static final Date convert(String source) throws ParseException{
return df.get().parse(source);
}
}
@Test
public void test2() throws Exception {
//解决多线程安全问题
Callable task = new Callable() {
@Override
public Date call() throws Exception {
return DateFormatThreadLocal.convert("20161121");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future future : results) {
System.out.println(future.get());
}
pool.shutdown();
}
1.8 以后就没有这些问题了,LocalDate、LocalTime、LocalDateTime 类是其中较重要的几个类,它们的实例是不可变的对象
@Test
public void test3() throws Exception {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
Callable task = new Callable() {
@Override
public LocalDate call() throws Exception {
LocalDate ld = LocalDate.parse("20161121", dtf);
return ld;
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future future : results) {
System.out.println(future.get());
}
pool.shutdown();
}
LocalDate、LocalTime、LocalDateTime 类是其中较重要的几个类,它们的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的本地日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法,也就是公历。
方法 |
描述 |
now() / * now(ZoneId zone) |
静态方法,根据当前时间创建对象/指定时区的对象 |
of() |
静态方法,根据指定日期/时间创建对象 |
getDayOfMonth()/getDayOfYear() |
获得月份天数(1-31) /获得年份天数(1-366) |
getDayOfWeek() |
获得星期几(返回一个 DayOfWeek 枚举值) |
getMonth() |
获得月份, 返回一个 Month 枚举值 |
getMonthValue() / getYear() |
获得月份(1-12) /获得年份 |
getHour()/getMinute()/getSecond() |
获得当前对象对应的小时、分钟、秒 |
withDayOfMonth()/withDayOfYear()/ withMonth()/withYear() |
将月份天数、年份天数、月份、年份修改为指定的值并返回新的对象 |
plusDays(), plusWeeks(), plusMonths(), plusYears(),plusHours() |
向当前对象添加几天、几周、几个月、几年、几小时 |
minusMonths() / minusWeeks()/ minusDays()/minusYears()/minusHours() |
从当前对象减去几月、几周、几天、几年、几小时 |
三者使用方法一样,演示使用 LocalDateTime
/**
* 不管做怎样的改变都会产生新的实例
*/
//1. LocalDate、LocalTime、LocalDateTime
@Test
public void test1(){
//获取当前时间
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
//指定时间
LocalDateTime ld2 = LocalDateTime.of(2016, 11, 21, 10, 10, 10);
System.out.println(ld2);
//在当前时间加20 年
LocalDateTime ldt3 = ldt.plusYears(20);
System.out.println(ldt3);
//在当前时间减 2 个月
LocalDateTime ldt4 = ldt.minusMonths(2);
System.out.println(ldt4);
System.out.println(ldt.getYear());//年
System.out.println(ldt.getMonthValue());//月
System.out.println(ldt.getDayOfMonth());//日
System.out.println(ldt.getHour());//时
System.out.println(ldt.getMinute());//分
System.out.println(ldt.getSecond());//秒
}
在处理时间和日期的时候,我们通常会想到年,月,日,时,分,秒。然而,这只是时间的一个模型,是面向人类的。第二种通用模型是面向机器的,或者说是连续的。在此模型中,时间线中的一个点表示为一个很大的数,这有利于计算机处理。在UNIX中,这个数从1970年开始,以秒为的单位;同样的,在Java中,也是从1970年开始,但以毫秒为单位。
java.time包通过值类型Instant提供机器视图,不提供处理人类意义上的时间单位。Instant表示时间线上的一点,而不需要任何上下文信息,例如,时区。概念上讲,它只是简单的表示自1970年1月1日0时0分0秒(UTC)开始的秒数。因为java.time包是基于纳秒计算的,所以Instant的精度可以达到纳秒级。(1 ns = 10-9 s) 1秒 = 1000毫秒 =10^6微秒=10^9纳秒
方法 |
描述 |
now() |
静态方法,返回默认UTC时区的Instant类的对象 |
ofEpochMilli(long epochMilli) |
静态方法,返回在1970-01-01 00:00:00基础上加上指定毫秒数之后的Instant类的对象 |
atOffset(ZoneOffset offset) |
结合即时的偏移来创建一个 OffsetDateTime |
toEpochMilli() |
返回1970-01-01 00:00:00到当前时间的毫秒数,即为时间戳 |
时间戳:是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
//2. Instant : 时间戳。 (使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值)
@Test
public void test2(){
Instant ins = Instant.now(); //默认使用 UTC 时区
System.out.println(ins);
//带偏移量运算 偏移8个时区
OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);
System.out.println(ins.getNano());
//改变时间戳 加5秒
Instant ins2 = Instant.ofEpochSecond(5);
System.out.println(ins2);
System.out.println("时间戳:(转成毫秒时间)"+ins.toEpochMilli());
}
2019-02-12T14:28:28.568Z
2019-02-12T22:28:28.568+08:00
568000000
1970-01-01T00:00:05Z
//3.
//Duration : 用于计算两个“时间”间隔
//Period : 用于计算两个“日期”间隔
@Test
public void test3(){
Instant ins1 = Instant.now();
System.out.println("--------------------");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
Instant ins2 = Instant.now();
Duration duration = Duration.between(ins1, ins2);
System.out.println("所耗费时间为:" + duration.toMillis()+"毫秒");
// duration.getSeconds();//获取秒
// duration.getNano();//获取纳秒
// duration.toMillis();//获取毫秒
LocalTime lt1 = LocalTime.now();
try {
Thread.sleep(1000);
}catch (InterruptedException e){ }
LocalTime lt2 = LocalTime.now();
System.out.println(Duration.between(lt1, lt2).toMillis());
System.out.println("---------------Period : 用于计算两个“日期”间隔-------------------");
LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.of(2011, 1, 1);
Period pe = Period.between(ld2, ld1);
System.out.println(pe.getYears());
System.out.println(pe.getMonths());
System.out.println(pe.getDays());
}
Java8中加入了对时区的支持,带时区的时间为分别为:
ZonedDate、ZoneTime、ZonedDateTime
其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式
例如:Asa/Shanghai等
Zoned:该类中包含了所有的时区信息
getAvailableZonelds():可以获取所有时区时区信息、
of(id):用指定的时区信息获取 Zoned对象
//6.ZonedDate、ZonedTime、ZonedDateTime : 带时区的时间或日期
@Test
public void test7(){
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt);
System.out.println("----------带时区的时间或日期--------------------");
LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime zdt = ldt2.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(zdt);
System.out.println("-------------------------------------");
ZonedDateTime zdt2 = ZonedDateTime.now(ZoneId.of("US/Pacific"));
System.out.println(zdt2);
}
@Test
public void test6(){
Set set = ZoneId.getAvailableZoneIds();
set.forEach(System.out::println);
}
2019-02-12T23:47:23.778
----------带时区的时间或日期--------------------
2019-02-12T23:47:23.779+08:00[Asia/Shanghai]
-------------------------------------
2019-02-12T07:47:23.781-08:00[US/Pacific]
上面的 +08:00 表示的是时差(与 UTC 相比 有8个小时的时差)
java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:
预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE
本地化相关的格式。如:ofLocalizedDate(FormatStyle.FULL)
自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss E”)
方法 |
描述 |
ofPattern(String pattern) |
静态方法,返回一个指定字符串格式的DateTimeFormatter |
format(TemporalAccessor t) |
格式化一个日期、时间,返回字符串 |
parse(CharSequence text) |
将指定格式的字符序列解析为一个日期、时间 |
//5. DateTimeFormatter : 解析和格式化日期或时间
@Test
public void test5(){
// DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
DateTimeFormatter dtf2 = DateTimeFormatter.ISO_DATE;
LocalDateTime ldt2 = LocalDateTime.now();
String str = ldt2.format(dtf2);
System.out.println(str);
System.out.println("-------------自定义格式-----------------------");
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
LocalDateTime ldt = LocalDateTime.now();
String strDate = ldt.format(dtf);
// String strDate = dtf.format(ldt); //两种都可以
System.out.println(strDate);
//解析回去
LocalDateTime newLdt = ldt.parse(strDate, dtf);
System.out.println(newLdt);
}
● TemporalAdjuster:时间校正器。有时我们可能需要获取 例如:将日期调整到“下个周日”等操作
● TemporalAdjusters:该类通过静态方法提供了大量的常用 TemporalAdjuster的实现。
例如获取下个周日:
LocalDate nextSundy = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
//4. TemporalAdjuster : 时间校正器
@Test
public void test4(){
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
//把 月份改成10月 其他的不变
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
System.out.println(ldt2);
//下一个周日的时间
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
//自定义:计算下一个工作日 的日期
LocalDateTime ldt5 = ldt.with((l) -> {
LocalDateTime ldt4 = (LocalDateTime) l;
DayOfWeek dow = ldt4.getDayOfWeek();
if(dow.equals(DayOfWeek.FRIDAY)){//如果是周五
return ldt4.plusDays(3); //加三天 三天后上班
}else if(dow.equals(DayOfWeek.SATURDAY)){//如果是周6
return ldt4.plusDays(2);//加两天
}else{
return ldt4.plusDays(1);//全部加一天
}
});
System.out.println(ldt5);
}
2019-02-12T23:04:23.877
2019-02-10T23:04:23.877
2019-02-17T23:04:23.877
2019-02-13T23:04:23.877
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Set;
import org.junit.Test;
public class TestLocalDateTime {
//6.ZonedDate、ZonedTime、ZonedDateTime : 带时区的时间或日期
@Test
public void test7(){
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt);
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
System.out.println(zdt);
}
@Test
public void test6(){
Set set = ZoneId.getAvailableZoneIds();
set.forEach(System.out::println);
}
//5. DateTimeFormatter : 解析和格式化日期或时间
@Test
public void test5(){
// DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
LocalDateTime ldt = LocalDateTime.now();
String strDate = ldt.format(dtf);
System.out.println(strDate);
LocalDateTime newLdt = ldt.parse(strDate, dtf);
System.out.println(newLdt);
}
//4. TemporalAdjuster : 时间校正器
@Test
public void test4(){
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
System.out.println(ldt2);
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
//自定义:下一个工作日
LocalDateTime ldt5 = ldt.with((l) -> {
LocalDateTime ldt4 = (LocalDateTime) l;
DayOfWeek dow = ldt4.getDayOfWeek();
if(dow.equals(DayOfWeek.FRIDAY)){
return ldt4.plusDays(3);
}else if(dow.equals(DayOfWeek.SATURDAY)){
return ldt4.plusDays(2);
}else{
return ldt4.plusDays(1);
}
});
System.out.println(ldt5);
}
//3.
//Duration : 用于计算两个“时间”间隔
//Period : 用于计算两个“日期”间隔
@Test
public void test3(){
Instant ins1 = Instant.now();
System.out.println("--------------------");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
Instant ins2 = Instant.now();
System.out.println("所耗费时间为:" + Duration.between(ins1, ins2));
System.out.println("----------------------------------");
LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.of(2011, 1, 1);
Period pe = Period.between(ld2, ld1);
System.out.println(pe.getYears());
System.out.println(pe.getMonths());
System.out.println(pe.getDays());
}
//2. Instant : 时间戳。 (使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值)
@Test
public void test2(){
Instant ins = Instant.now(); //默认使用 UTC 时区
System.out.println(ins);
OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);
System.out.println(ins.getNano());
Instant ins2 = Instant.ofEpochSecond(5);
System.out.println(ins2);
}
//1. LocalDate、LocalTime、LocalDateTime
@Test
public void test1(){
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
LocalDateTime ld2 = LocalDateTime.of(2016, 11, 21, 10, 10, 10);
System.out.println(ld2);
LocalDateTime ldt3 = ld2.plusYears(20);
System.out.println(ldt3);
LocalDateTime ldt4 = ld2.minusMonths(2);
System.out.println(ldt4);
System.out.println(ldt.getYear());
System.out.println(ldt.getMonthValue());
System.out.println(ldt.getDayOfMonth());
System.out.println(ldt.getHour());
System.out.println(ldt.getMinute());
System.out.println(ldt.getSecond());
}
}
Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。此外,反射也得到了加强,在Java8中能够得到方法参数的名称。这会简化标注在方法参数上的注解。
@Target({TYPE, FIELD, METHOD, PARAMETER,PACKAGE, CONSTRUCTOR, LOCAL_VARIABLE,TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {
String[] value();
}
应用场景:
public Person(@MyAnnotation(value="notnull")String name){
this.name = name;
}
|------List子接口:存储序的、可重复的数据 ---->"动态"数组
|-----ArrayList:作为List的主要实现类;线程不安全的,效率高;底层使用数组实现
(Collections中定义了synchronizedList(List list)将此ArrayList转化为线程安全的)
|-----LinkedList:对于频繁的插入、删除操作,我们建议使用此类,因为效率高;内存消耗较ArrayList大;底层使用双向链表实现
|-----Vector:List的古老实现类;线程安全的,效率低;底层使用数组实现
然后可以分析一下ArrayList和LinkedList的底层源码实现
补充:你不可不知的数据结构
jdk7:
ArrayList list = new ArrayList();//初始化一个长度为10的Object[] elementData
sysout(list.size());//返回存储的元素的个数:0
list.add(123);
list.add(345);
...
当添加第11个元素时,需要扩容,默认扩容为原来的1.5倍。还需要将原有数组中的数据复制到新的数组中。
删除操作:如果删除某一个数组位置的元素,需要其后面的元素依次前移。
remove(Object obj) / remove(int index)
jdk8:
ArrayList list = new ArrayList();//初始化一个长度为0的Object[] elementData
sysout(list.size());//返回存储的元素的个数:0
list.add(123);//此时才创建一个长度为10的Object[] elementData
list.add(345);
...
当添加第11个元素时,需要扩容,默认扩容为原来的1.5倍。还需要将原有数组中的数据复制到新的数组中。
开发时的启示:
1. 建议使用:ArrayList list = new ArrayList(int length);
2. jdk8延迟了底层数组的创建:内存的使用率;对象的创建更快
LinkedList:底层使用双向链表存储添加的元素
void linkLast(E e) {
final Node l = last;
final Node newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
内部类体现:
private static class Node {
E item;
Node next;
Node prev;
}
HashMap:Map的主要实现类;线程不安全的,效率高;可以存储null的key和value
(存储结构:jdk7.0 数组+链表; jdk8.0 数组+链表+红黑树)
Hashtable:Map的古老实现类;线程安全的,效率低;不可以存储null的key和value
HashMap map = new HashMap();//底层创建了长度为16的Entry数组
向HashMap中添加entry1(key,value),需要首先计算entry1中key的哈希值(根据key所在类的hashCode()计算得到),此哈希值经过处理以后,得到在底层Entry[]数组中要存储的位置i.如果位置i上没有元素,则entry1直接添加成功。如果位置i上已经存在entry2(或还有链表存在的entry3,entry4),则需要通过循环的方法,依次比较entry1中key和其他的entry是否equals.如果返回值为true.则使用entry1的value去替换equals为true的entry的value.如果遍历一遍以后,发现所有的equals返回都为false,则entry1仍可添加成功。entry1指向原有的entry元素。
默认情况下,如果添加元素的长度 >= DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR (临界值threshold默认值为12)且新要添加的数组位置不为null的情况下,就进行扩容。默认扩容为原有长度的2倍。将原有的数据复制到新的数组中。
jdk 8.0 :
1.HashMap map = new HashMap();//默认情况下,先不创建长度为16的数组。2.当首次调用map.put()时,再创建长度为16的数组
3.当数组指定索引位置的链表长度>8时,且map中的数组的长度> 64时,此索引位置上的所有entry使用红黑树进行存储。而jdk 7 中没有红黑树结构
4.新添加的元素如果与现有的元素以链表方式存储的时候:“七上八下”:jdk7:新添加的当链表头,jdk8:新添加的当链表尾
JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)
JDK 8版本发布以后:HashMap是数组+链表+红黑树实现。
注:jdk1.7 以前,如果运算得到同一个位置,后加入的放入这个链表前面(头插法)。为了避免碰撞,重新 equals 和 hashcode 方法要保持一致,对象内容一样生成的 hashcode 值一样,equals 方法返回 true。极端情况下,HashMap 可能成为单链表了。
jdk1.8 如果扩容(重排序,换位置),不会再从新计算元素(计算 hashcode 值)的位置了 ,只需要在原来hash表的种长度,加上当前所在的位置即可。
负载因子的大小决定了HashMap的数据密度,负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长,造成查询或插入时的比较次数增多,性能会下降。负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预设大一点的空间。
注:负载因子值的大小默认0.75,即到了原来的 75% 就开始扩容。
LinkedHashMap继承于HashMap,在HashMap底层结构的基础上额外添加了一对链表:
static class Entry extends HashMap.Node {
Entry before, after;
}
public static void main(String[] args) {
List list = new ArrayList();
list.add(1):
list.add(2);
list.add(3);
updateList(list);
System.out.println(list);
}
private static void updateList(List list) {
list.remove(2);
}
HashSet set = new HashSet();
Person p1 = new Person(1001,"AA");
Person p2 = new Person(1002,"BB");
set.add(p1);
set.add(p2);
p1.name = "CC";
set.remove(p1);
System.out.println(set);
set.add(new Person(1001,"CC"));
System.out.println(set);
set.add(new Person(1001,"AA"));
System.out.println(set);
其中,其中Person类中重写了hashCode()和equal()方法
参考:https://blog.csdn.net/lixiaobuaa/article/details/81099838