Java8特性总结
主要内容:
- Lambda表达式
- 函数式接口
- 方法引用与构造器引用
- Stream API
- 接口中的默认方法与静态方法
- 新时间日记API
- 其他新特性
Java8新特性简介
- 速度更快 底层数据结构改变 底层内存结构改变 方法区变成MetaSpace元空间 使用物理内存 如果元空间将要满的时候 垃圾回收机制才会进行回收 所以效率会提升
- JVM调优:MetaSpaceSize MaxMetaSpaceSize 配置元空间大小
- 代码更少(新增加了新的语法Lambda表达式)
- 强大的Stream API(操作数据像使用sql操作一样简单)
- 便于并行
- 最大化减少空指针异常 Optional(提供容器类)
其中最为核心的为Lambda表达式与Stream API
Lambda表达式
- Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,是Java的语言表达能力得到了提升。
/*数据集合*/
List<Employee> employees = Arrays.asList(new Employee("张三", 19, 2300.00), new Employee("赵四", 20, 6300.00),
new Employee("王五", 18, 3300.00), new Employee("李六", 35, 7200.00));
/**
* 优化方式四:Stream + Lambda表达式
*/
@Test
public void test7() {
/*过滤掉工资大于5000的人并输出一条数据*/
employees.stream().filter((e)->e.getSalary()>=5000)
.limit(1)
.forEach(System.out::println);
System.out.println("----------------------");
/*值输出数据中所有人的名字*/
employees.stream()
.map(Employee::getName)
.forEach(System.out::println);
}
package java8day01.test2;
import java.util.Comparator;
import java.util.function.Consumer;
import java.util.jar.Attributes.Name;
import org.junit.Test;
/**
* Lambda表达式的基础语法: Java8中引入了一个新的操作符“->”该操作符称为箭头操作符或 Lambda操作符
* 箭头操作符将Lambda表达式拆分成两部分: 左侧:Lambda表达式的参数列表 右侧:Lambda表达式中所需要执行的功能,即Lambda体
*
* 语法格式一:无参数,无返回值 ()-> System.out.println("Hello Lambda");
*
* 语法格式二:有一个参数,并且无返回值(name) -> System.out.println("Hello " + name);
*
* 语法格式三:若只有一个参数,小括号可以省略不写name -> System.out.println("Hello " + name);
*
* 语法格式四:有两个以上的参数,并且Lambda体中有多条语句
* Comparator<Integer> comparator = (x,y)->{
System.out.println("这是Lambda函数式接口");
return Integer.compare(x, y);
};
*
* 语法格式五:若Lambda体中只有一条语句,return和大括号都可以省略不写
* Comparator<Integer> comparator2 = (x,y)->
Integer.compare(x, y);
语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器
通过上下文推断出,数据类型,即“类型推断”
*
* 总结:
* 左右遇-括号省
* 左侧推断类型省
* 能省则省
*
*
* 二:Lambda表达式需要“函数式接口”的支持
* 函数式接口:接口中只有一个抽象方法的接口,称为函数接口。可以使用注解@FunctionalInterface修饰
* 可以检查是否是函数式接口
*
* @author 86241
*
*/
public class TestLambda2 {
/**
* 无参数,无返回值
*/
@Test
public void test1() {
int num = 0;// jdk1.7以前必须是final jdk1.8默认自动在前方加上final
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Hello World" + num);
}
};
runnable.run();
System.out.println("-----------------");
Runnable runnable2 = () -> System.out.println("Hello Lambda");
runnable2.run();
}
/**
* 有一个参数,没有返回值
*/
@Test
public void test2() {
/*对接口中的方法进行实现*/
Consumer<String> con = name -> System.out.println("Hello " + name);
/*调用接口中的方法*/
con.accept("yjlg");
}
/**
* 多个参数,有返回值
*/
@Test
public void test3() {
/**
* Comparator比较函数 compare方法 前者大于后者返回1 否则 返回-1
*/
Comparator<Integer> comparator = (x,y)->{
System.out.println("这是Lambda函数式接口");
return Integer.compare(x, y);
};
System.out.println(comparator.compare(30, 20));
Comparator<Integer> comparator2 = (x,y)->
Integer.compare(x, y);
System.out.println("-----------------");
System.out.println(comparator2.compare(10, 20));
}
@Test
public void test4() {
String[] strs = {"aaa", "bbb", "ccc"};
}
/**
* 需求:对一个数进行运算
*/
@Test
public void test5() {
System.out.println(operation(10, num->num));
}
public Integer operation(Integer num,MyFun mf) {
return mf.getValue(num);
}
}
Lambda表达式练习
package java8day01.test3;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.StreamHandler;
import org.junit.Test;
import java8day01.Employee;
/**
* Lambda练习
*
* @author 86241
*
*/
public class TestLambda3 {
/**
* 练习一:调用Collections.sort()方法,通过定制排序比较两个Employee(
* 先按年龄比较,年龄相同按姓名比较),使用Lambda作为参数传递
*/
List<Employee> employees = Arrays.asList(new Employee("张三", 19, 2300.00), new Employee("赵四", 20, 6300.00),
new Employee("王五", 18, 3300.00), new Employee("李六", 35, 7200.00));
@Test
public void test1() {
/*对集合进行排序*/
Collections.sort(employees,(e1,e2)->{
if (e1.getAge()==e2.getAge()) {
return e1.getName().compareTo(e2.getName());
}else {
return Integer.compare(e1.getAge(), e2.getAge());
}
});
employees.forEach(System.out::println);
}
/**
* 声明函数式接口,接口中声明抽象方法,public String getValue(String str);
* 声明类TestLambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为
* 方法的返回值。
* 再将一个字符串的第2个和第4个索引位置进行截取子串
*/
@FunctionalInterface
interface MyFunction {
public String getValue(String str);
}
/*用于处理字符串的方法*/
public String strHandler(String str,MyFunction mf) {
return mf.getValue(str);
}
@Test
public void test() {
System.out.println(strHandler("hello", str->str.toUpperCase()));
System.out.println("------------------");
System.out.println(strHandler("hello", str->str.substring(1, 4)));
}
/**
* 声明一个带两个泛型的函数式接口,泛型类型为<T,R> T为参数,R为返回值
* 接口中声明对应抽象方法
* 在TestLambda类中声明方法,使用接口作为参数,计算两个long类型参数的和
* 再计算两个long型参数的乘积
*/
@FunctionalInterface
interface MyFunction2<T,R>{
public R getValue(T t,R r);
}
/*需求:对两个long类型数据进行处理*/
public void op(Long l1, Long l2, MyFunction2<Long, Long> mf) {
System.out.println(mf.getValue(l1, l2));
}
@Test
public void test2() {
/*对两个数的和*/
op(100L, 200L, (l1,l2)->l1+l2);
op(100L, 200L, (l1,l2)->l1*l2);
}
}
四大内置核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer消费类型接口 | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) |
Supplier函数型接口 | 无 | T | 返回类型为T的对象,包含方法:T get() |
Punction<T, R>函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果,结果是R类型的对象。包含方法:R apply(T t) |
Predicate判定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法boolean test(T t) |
package java8day02;
/**
* java8 内置的四大核心函数式接口
*
* Consumer<T> 消费型接口
* void accept(T t);
*
* Supplier<T> 供给型接口
* T get();
*
* Function<T, R> 函数型接口
* R apply(T t);
*
* Predicate<T> 断言型接口
* boolean test(T t);
* @author 86241
*
*/
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;
public class TestLambda {
/*
* Consumer<T> 消费型接口
*/
@Test
public void test1() {
happy(10000, (money)->System.out.println("买冰棍花了"+money/1000+""));
}
public void happy(double money, Consumer<Double> con) {
con.accept(money);
}
/*
* Supplier<T> 供给型接口
*/
@Test
public void test2() {
List<Integer> list = getNums(10, ()->(int)(Math.random()*100));
list.forEach(System.out::println);
}
/*需求:产生指定个数的整数,并放入集合中*/
public List<Integer> getNums(int nums, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < nums; i++) {
list.add(sup.get());
}
return list;
}
/*
* Function<T, R> 函数型接口
*/
@Test
public void test3() {
String email = "junwei5683@qq.com";
String string=strHandler(email, (e)->e.substring(e.indexOf('@')+1, e.indexOf('.')));
System.out.println(string);
}
/*需求:用于处理字符串*/
public String strHandler(String str, Function<String, String> fun) {
return fun.apply(str);
}
/*
* Predicate 断言型接口
*/
@Test
public void test4() {
List<String> list = Arrays.asList("hello","www","ok","no");
List<String> str = filterStr(list, (s)->s.length()>=3);
str.forEach(System.out::println);
}
/*需求:将满足条件的字符串添加到集合中放回*/
public List<String> filterStr(List<String> list, Predicate<String> pre){
List<String> strings = new ArrayList<>();
for (String string : list) {
if (pre.test(string)) {
strings.add(string);
}
}
return strings;
}
}
方法引用与构造器引用
package java8day02;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.junit.Test;
import java8day01.Employee;
/**
* 一、方法引用:若Lambda体中的内容有方法已经实现了 我们可以使用方法引用
* 可以理解为方法引用是Lambda表达式的另外一种表现形式
*
* 主要有三种语法格式:
* 注意:
* 1.Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中
* 抽象方法的函数列表和返回值类型保持一致!
*
* 2.若Lambda参数列表中的第一个参数是实例方法的调用者,而
* 第二个参数是实例方法的参数时,可以使用ClassName::method
*
* 对象::实例方法名
*
* 类::静态方法名
*
* 类::实例方法名
*
* 二、构造器引用
*
* 格式:
*
* className::new
*
* 注意:需要调用的构造器的参数列表要与函数式接口中抽象
* 方法的参数列表保持一致!
*
* 三、数组引用
*
* type::new
* @author 86241
*
*/
public class TestMethodRef {
/*数组引用*/
@Test
public void test6() {
Function<Integer, String[]> fun = String[]::new;
String[] strs = fun.apply(10);
System.out.println(strs.length);
}
/*构造器引用*/
@Test
public void test5() {
Supplier<Employee> supplier = ()->new Employee();
/*构造器方式*/
//自动匹配无参构造器 自动匹配参数的个数 取决于接口中的参数个数
Supplier<Employee> supplier2 = Employee::new;
}
@Test
public void test1() {
/*消费型接口*/
Consumer<String> con = System.out::println;
con.accept("hello world");
}
@Test
public void test2() {
Supplier<String> sup = ()->"hello world";
System.out.println(sup.get());
Employee employee = new Employee();
employee.setName("张三");
Supplier<String> sup1 = employee::getName;
System.out.println(sup1.get());
}
@Test
public void test3() {
Comparator<Integer> com = Integer::compare;
System.out.println(com.compare(10, 20));
}
@Test
public void test4() {
BiPredicate<String, String> bPredicate = (x,y)->x.equals(y);
System.out.println(bPredicate.test("abc", "acd"));
System.out.println("-----------------");
/*类名调用实例方法*/
BiPredicate<String, String> bPredicate2 = String::equals;
System.out.println(bPredicate2.test("abc", "abc"));
}
}
Stream API
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
注意:
- Stream自己不会存储元素
- Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
- Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
package java8day01.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
import java8day01.Employee;
/**
* 一、Stream的三个操作步骤:
* 1.创建Stream
* 2.中间操作
* 3.终止操作
* @author 86241
*
*/
public class TestStreamAPI {
//创建Stream
@Test
public void test1() {
/*1.可以通过Collection系列集合提供的Stream()或parallelStream*/
List<String> list = new ArrayList<>();
/*创建流*/
Stream<String> stream = list.stream();
/*2.通过Arrays中的静态方法Stream()获取数组流*/
Employee[] employees = new Employee[10];
Stream<Employee> stream2 = Arrays.stream(employees);
/*3.通过Stream中的静态方法of*/
Stream<String> stream3 = Stream.of("aa","bb","cc");
/*4.创建无限流*/
//迭代
Stream<Integer> stream4 = Stream.iterate(0, (x)->x+2);
stream4.limit(10).forEach(System.out::println);
/*生成*/
Stream.generate(()->Math.random()).limit(10)
.forEach(System.out::println);;
}
}
Stream中间操作和映射
package java8day01.stream;
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;
import java8day01.Employee;
/**
* Stream中间操作
* @author 86241
*
*/
public class TestStreamAPI2 {
/**
* 中间操作
* filter--接收Lambda,从流中排除某些元素
* limit(n)--截断流,使其不超过给定数量
* skip(n)--跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
* distinct--筛选,通过流所产生元素的hashCode()和equals()去除重复元素
*
*/
List<Employee> employees = Arrays.asList(new Employee("张三", 19, 2300.00), new Employee("赵四", 20, 6300.00),
new Employee("王五", 35, 7200.00), new Employee("王五", 35, 7200.00));
@Test
public void test1() {
/*过滤掉年龄大于19的人*/
//内部迭代
employees.stream().filter((e)->e.getAge()>19)
.filter((e)->e.getSalary()>3000)
.skip(1).limit(1)
.forEach(System.out::println);
/*都重复 才进行筛选*/
employees.stream().distinct()
.forEach(System.out::println);
}
/**
* 映射
* map--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,
* 该函数会被应用到每个元素上,并将其映射成一个新的元素。
*
* flatMap--接收一个函数作为参数,将流中的每个值都映射成另一个流,然后把
* 所有流连成一个流。
*/
List<String> list = Arrays.asList("aa","bb","cc");
@Test
public void test5() {
/*创建一个字符串集合*/
/*创建流 形成map映射 截取第一个字符串 然后将其转为大写*/
list.stream().map((str)->str.substring(0, 1))
.map((str)->str.toUpperCase())
.forEach(System.out::println);
System.out.println("-----------------");
employees.stream().filter((e)->e.getAge()>19)
.map(Employee::getName)
.distinct()
.forEach(System.out::println);
}
/*将字符串集合以字符的形式输出*/
@Test
public void test6() {
/*map*/
Stream<Stream<Character>> stream = list.stream().map(TestStreamAPI2::filterCharacter);
stream.forEach((sm)->{
sm.forEach(System.out::println);
});
System.out.println("-------------");
/*flatMap*/
Stream<Character> stream2 = list.stream().flatMap(TestStreamAPI2::filterCharacter);
stream2.forEach(System.out::println);
}
public static Stream<Character> filterCharacter(String str){
ArrayList<Character> list = new ArrayList<>();
char[] charArray = str.toCharArray();
for (char c : charArray) {
list.add(c);
}
return list.stream();
}
}
Stream排序
package java8day01.stream;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import java8day01.Employee;
/**
* Stream排序特性
*
* @author 86241
*
*/
public class TestStreamAPI3 {
/**
* 排序 sorted()--自然排序(Comparable) sorted(Comparator com)--定制排序(Comparator)
*/
List<Employee> employees = Arrays.asList(new Employee("张三", 19, 2300.00), new Employee("赵四", 20, 6300.00),
new Employee("王五", 18, 3300.00), new Employee("李六", 35, 7200.00));
List<String> list = Arrays.asList("cc", "rr", "aa", "bb", "ee");
@Test
public void test1() {
/*将集合中的字符串按字典顺序进行排序 自然排序*/
list.stream().sorted().forEach(System.out::println);
System.out.println("-----------------");
/*先比较年龄 如果相等 然后对姓名进行比较 进行排序 输出*/
employees.stream().sorted((e1, e2) -> {
if (e1.getAge()==e2.getAge()) {
return e1.getName().compareTo(e2.getName());
} else {
return Integer.compare(e1.getAge(), e2.getAge());
}
}).forEach(System.out::println);
}
}
Stream的终止操作
package java8day01.stream;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.junit.Test;
import java8day01.Employee;
import java8day01.Employee.Status;
/**
* 终止操作
*
* @author 86241
*
*/
public class TestStreamAPI4 {
/*
* 查找与匹配 allMatch--检查是否匹配所有元素
* anyMatch--检查是否至少匹配一个元素
* noneMAtch--检查是否没有匹配所有元素
* findFirst--返回第一个元素
* findAny--返回当前流中的任意元素
* count--返回流中元素的总个数
* max--返回流中最大值
* min--返回流中最小值
*/
List<Employee> employees = Arrays.asList(new Employee("张三", 19, 4300.00, Status.BUSSY),
new Employee("赵四", 20, 6300.00, Status.FREE), new Employee("王五", 18, 3300.00, Status.VOCATION),
new Employee("李六", 35, 7200.00,Status.BUSSY));
List<String> list = Arrays.asList("cc", "rr", "aa", "bb", "ee");
@Test
public void test1() {
/*检查是否匹配所有元素 检查状态是否都为BUSSY*/
boolean b1 = employees.stream()
.allMatch((e)->e.getStatus().equals(Status.BUSSY));
System.out.println("检查是否匹配所有元素:"+b1);
System.out.println("---------");
boolean b2 = employees.stream()
.anyMatch((e)->e.getStatus().equals(Status.BUSSY));
System.out.println("检查是否至少匹配一个元素:"+b2);
System.out.println("---------");
boolean b3 = employees.stream()
.noneMatch((e)->e.getStatus().equals(Status.BUSSY));
System.out.println("检查是否没有匹配元素:"+b3);
System.out.println("------------");
/*按工资从低到高排序*/
employees.stream()
.sorted((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()))
.forEach(System.out::println);;
/*返回集合中的第一个元素*/
Optional<Employee> first = employees.stream().findFirst();
System.out.println("返回第一个元素:"+first.get());
System.out.println("-----------------");
Optional<Employee> findAll = employees.stream().findAny();
System.out.println("返回当前流中的任意元素:"+findAll);
System.out.println("------------------");
long count = employees.stream().count();
System.out.println("返回当前流中元素的总个数:"+count);
System.out.println("-----------------");
Optional<Employee> max = employees.stream().max((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println("返回流中的最大值:"+max.get());
System.out.println("------------------");
Optional<Employee> min = employees.stream().min((e1,e2)->Integer.compare(e1.getAge(), e2.getAge()));
System.out.println("返回流中的最小值:"+min.get());
}
}
reduce&collect
/**
* reduce(T identity, BinaryOperator)
* reduce(BinaryOperator)
* 可以将流中的元素反复结合起来,得到一个值
*/
@Test
public void test2() {
/*计算集合中元素总和*/
List<Integer> asList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
/*使用reduce(T identity, BinaryOperator) (起始值, 二元运算)*/
/*
* 0 起始值为0 作为x进行二元运算
* 将集合中的 第一个元素作为y进行运算
* 然后将其结果作为x再次进行二元运算
* 以此类推
*/
Integer reduce = asList.stream().reduce(0, (x,y)->x+y);
System.out.println(reduce.intValue());
System.out.println("--------------");
/*
* 需求:计算出公司中的工资总额
* 因为该结果有可能为空
* 所以要用Optional来接收
* sum是Java8中在Double类中心添加的静态方法
* map提取数据 reduce进行数据处理
*/
Optional<Double> reduce2 = employees.stream().map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(reduce2.get());
}
/*
* 收集
* collect--将流转换为其他形式。接收一个Collector接口的实现,
* 用于给Stream中元素汇总的方法
*/
@Test
public void test3() {
/*
* 需求:将公司中员工的名字提取出来,添加到一个集合中
* Collector收集器
* Collectors实现类 将结果添加到集合中
*/
List<String> collect = employees.stream().map(Employee::getName)
.collect(Collectors.toList());
collect.forEach(System.out::println);
System.out.println("---------------");
/*将结果放到set集合中*/
/*set集合 将重复元素进行覆盖*/
Set<String> set = employees.stream().map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("-------------");
/*将结果放到HashSet中*/
/*HashaSet也能达到去重的效果*/
employees.stream().map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new))
.forEach(System.out::println);;
}
@Test
public void test4() {
/*获取集合中元素的总数*/
long count = employees.stream().map(Employee::getName)
.count();
System.out.println(count);
System.out.println("--------------");
Long collect = employees.stream().map(Employee::getName)
.collect(Collectors.counting());
System.out.println(collect);
System.out.println("--------------------");
/*计算员工的平均工资*/
Double collect2 = employees.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(collect2);
System.out.println("--------------");
/*计算元素个数 总和 平均值 最大值 最小值*/
DoubleSummaryStatistics collect3 = employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(collect3);
employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println("----------------");
/*计算公司员工工资总和*/
Double collect4 = employees.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(collect4);
System.out.println("-----------------");
/*计算工资中最大值*/
Optional<Double> max = employees.stream().map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max);
System.out.println("------------");
/*计算工资中最小值*/
Optional<Employee> min = employees.stream()
.collect(Collectors.minBy((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(min);
}
/*
* 分组
* groupingBy
*/
@Test
public void test5() {
/*按工作状态进行分组*/
Map<Status, List<Employee>> collect = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(collect);
System.out.println("----------------");
Collection<List<Employee>> values = collect.values();
values.forEach(System.out::println);
}
/*
* 输出结果:
* {VOCATION=[Employee [name=王五, age=29, salary=3300.0, status=VOCATION]], BUSSY=[Employee [name=张三, age=19, salary=4300.0, status=BUSSY], Employee [name=李六, age=35, salary=7200.0, status=BUSSY]], FREE=[Employee [name=张三, age=20, salary=6300.0, status=FREE]]}
* ----------------
* [Employee [name=王五, age=29, salary=3300.0, status=VOCATION]]
* [Employee [name=张三, age=19, salary=4300.0, status=BUSSY], Employee [name=李六, age=35, salary=7200.0, status=BUSSY]]
* [Employee [name=张三, age=20, salary=6300.0, status=FREE]]
*/
/*
* 多级分组
* groupingBy
* */
@Test
public void test6() {
/*
* 需求:
* 先按状态进行分组
* 然后按年龄进行分组
* 按工资高低进行分组
*/
Map<Status, Map<String, Map<String, List<Employee>>>> collect = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e)->
e.getAge()<20?"青年":e.getAge()<30?"中年":"老年"
,Collectors.groupingBy((e)->e.getSalary()<5000?"低工资":e.getSalary()<7000?"中等工资":"高工资"))));
/*迭代器进行迭代*/
Iterator<Map<String, Map<String, List<Employee>>>> iterator = collect.values().iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
/*
* 输出结果:
* {中年={低工资=[Employee [name=王五, age=29, salary=3300.0, status=VOCATION]]}}
* {中年={中等工资=[Employee [name=张三, age=20, salary=6300.0, status=FREE]]}}
* {青年={低工资=[Employee [name=张三, age=19, salary=4300.0, status=BUSSY]]}, 老年={高工资=[Employee [name=李六, age=35, salary=7200.0, status=BUSSY]]}}
*/
/*
* 分区
* partitioningBy
*/
@Test
public void test7() {
Map<Boolean, List<Employee>> collect = employees.stream()
.collect(Collectors.partitioningBy((e)->e.getAge()>20));
System.out.println(collect);
System.out.println("------------------");
Iterator<List<Employee>> iterator = collect.values().iterator();
while(iterator.hasNext())
System.out.println(iterator.next());
}
/*
* 输出结果:
* {false=[Employee [name=张三, age=19, salary=4300.0, status=BUSSY], Employee [name=张三, age=20, salary=6300.0, status=FREE]], true=[Employee [name=王五, age=29, salary=3300.0, status=VOCATION], Employee [name=李六, age=35, salary=7200.0, status=BUSSY]]}
* ------------------
* [Employee [name=张三, age=19, salary=4300.0, status=BUSSY], Employee [name=张三, age=20, salary=6300.0, status=FREE]]
* [Employee [name=王五, age=29, salary=3300.0, status=VOCATION], Employee [name=李六, age=35, salary=7200.0, status=BUSSY]]
*/
/*
* 连接收集查询
* joining
*/
@Test
public void test8() {
String string = employees.stream().map(Employee::getName)
.collect(Collectors.joining(",","===","==="));
System.out.println(string);
}
/*
* 输出结果:
* ===张三,张三,王五,李六===
*/
练习
package java8day01.stream;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.Test;
import java8day01.Employee;
import java8day01.Employee.Status;
public class StreamTest {
/**
* 练习1:
* 给定一个数字列表,如何返回一个由
* 每个数字的平方构成的列表呢?
* 给定[1,2,3,4,5],应该返回[1,4,9,16,25]
*/
@Test
public void test1() {
/*创建集合*/
List<Integer> list = Arrays.asList(1,2,3,4,5);
List<Integer> collect = list.stream().map((num)->num*num).collect(Collectors.toList());
System.out.println(collect);
}
/**
* 练习2:
* 使用map和reduce数一数集合中有多少个Employee
*/
List<Employee> employees = Arrays.asList(new Employee("张三", 19, 4300.00, Status.BUSSY),
new Employee("张三", 20, 6300.00, Status.FREE), new Employee("王五", 29, 3300.00, Status.VOCATION),
new Employee("李六", 35, 7200.00,Status.BUSSY));
@Test
public void test2() {
Optional<Integer> reduce = employees.stream().map((e)->1).reduce((Integer::sum));
System.out.println(reduce.get());
}
}
并行流与顺序流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作。
Stream API可以声明性地通过parallel()与sequential()在并行流与顺序流之间进行切换。
了解Fork/Join框架
**Fork/Join框架:**就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行join汇总。
Fork/Join框架与传统线程池的区别
采用“工作窃取”模式(work-stealing):
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。
相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上,在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态,而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行,那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行。这种方式减少了线程的等待时间,提高性能。
Java7中的使用
package java8day01.stream;
import java.util.concurrent.RecursiveTask;
/**
* ForkJoin框架 RecursiveTask<T> 无返回值 Recursive 递归
*
* @author 86241
*
*/
public class ForkJoinCalculate extends RecursiveTask<Long> {
/**
*
*/
/* 临界值 */
private static final long serialVersionUID = 10000000000L;
/* 开始 */
private long start;
/* 末尾 */
private long end;
/* 拆分个数 */
private static final long THRESSHOLD = 10000;
@Override
protected Long compute() {
/* 计算出数据的长度 */
long length = end - start;
/* 判断拆分 */
if (length <= THRESSHOLD) {
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();
}
}
/*构造方法*/
public ForkJoinCalculate(long start, long end) {
super();
this.start = start;
this.end = end;
}
}
package java8day01.stream;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import org.junit.Test;
/**
* 测试ForkJoin
* @author 86241
*
*/
public class TestForkJoin {
@Test
public void test() {
/*建立ForkJoin线程池*/
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinCalculate(0, 10000000L);
/*返回总和*/
Long sum = pool.invoke(task);
System.out.println(sum);
}
}
Java8中的使用
@Test
public void test3() {
Instant start1 = Instant.now();
long l1 = LongStream.rangeClosed(0, 100000000000L)
.parallel()
.reduce(0,Long::sum);
System.out.println(l1);
Instant end1 = Instant.now();
System.out.println("Java8并行流耗费时间:"+Duration.between(start1, end1).getSeconds()+"秒");
System.out.println("-------------------");
Instant start2 = Instant.now();
Long l2 = (1+100000000000L)*(100000000000L/2);
System.out.println(l2);
Instant end2 = Instant.now();
System.out.println("算法耗费时间:"+Duration.between(start2, end2).getNano()+"纳秒");
}
Optional类
Optional类(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):如果调用对象包含值,返回该值,否则返回获取的值
- map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回OPtional.empty()
- flatMap(Function mapper):与map类似,要求返回值必须是Optional
package test.optional;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.LongStream;
import javax.lang.model.type.ErrorType;
import org.junit.Test;
import java8day01.Employee;
/**
* - Optional.of(T t):创建一个Optional实例。
- Optional.empty():创建一个空的Optional实例
- Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例
- isPresent():判断是否包含值
- orElse(T t):如果调用对象包含值,返回该值,否则返回t
- orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回获取的值
- map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回OPtional.empty()
- flatMap(Function mapper):与map类似,要求返回值必须是Optional
* @author 86241
*
*/
public class TestOptional {
@Test
public void test1() {
Optional<Employee> of = Optional.of(new Employee());
Employee employee = of.get();
System.out.println(employee);
}
@Test
public void test2() {
Optional<Object> empty = Optional.empty();
Optional<Employee> ofNullable = Optional.ofNullable(null);
System.out.println(ofNullable.isPresent());
Optional<Employee> em = Optional.ofNullable(new Employee());
System.out.println(ofNullable.orElse(new Employee()));
}
/*
* 使用并行流求解阶乘
*/
@Test
public void test3() {
long reduce = LongStream.range(1, 60L)
.parallel()
.reduce(1,(n1,n2)->n1*n2);
System.out.println(reduce);
List<Integer> list = Arrays.asList(1,2,3,4,5);
Integer reduce2 = list.stream().parallel()
.reduce(1,(n1,n2)->n1*n2);
System.out.println(reduce2);
}
@Test
public void test4() {
List<String> asList = Arrays.asList("123@qq.com","456@163.com","789@sina.com");
asList.stream()
.parallel()
.map((str)->str.substring(str.lastIndexOf('@')+1, str.lastIndexOf('.')))
.forEach(System.out::println);
System.out.println("-----------------");
asList.stream()
.parallel()
.map((str)->str.substring(str.indexOf('@')+1, str.indexOf('.')))
.forEach(System.out::println);
System.out.println("--------------------");
List<String> list = Arrays.asList("15633165683","13930690258","17603167640");
list.stream().parallel()
.map((str)->str.substring(0, 3))
.map((str)->{
if (str.equals("156")||str.equals("176")) {
return "联通号码"+str;
}else {
return "移动号码"+str;
}
}).forEach(System.out::println);
}
}
接口中的默认方法与静态方法
/*定义接口*/
public interface MyFun{
/*接口中声明默认方法*/
default String getName(){
return "123";
}
}
/*创建实体类*/
public class MyClass{
public String getName(){
return "456";
}
}
/*创建实体类 继承父类 并实现接口*/
public class SubClass extends MyClass implements MyFun{
}
/*创建测试方法*/
public class TestDefaultIntegerface{
public static void main(String[] args){
/*实例化对象*/
SubClass sc = new SubCalss();
/*调用方法*/
sc.getName();
/*输出456*/
}
}
/*定义接口*/
public interface MyInterface{
default String getName(){
return "789";
}
}
public class sub implements MyFun, MyInterface{
/*其实现接口中又相同的默认方法 所以必须去重写其中的一个方法*/
@Override
public String getName(){
return MyFun.super.getName();
}
}
接口中的默认方法
**接口默认方法的“类优先”原则 **
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名方法时
- 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
- 接口冲突。如果一个父类接口提供一个默认方法,而另一个接口也提供了一个具有相同的名称和参数列表的方法(不管方法是否默认方法),那么必须覆盖该方法来解决冲突。
新时间日期API
package test.date;
import java.util.Date;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class DateFormatThredLocal {
/*设置日期接收格式*/
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);
}
}
package test.date;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.Test;
/**
* 线程池的使用 处理 日期
* 传统时间格式的线程安全问题
* @author 86241
*
*/
public class TestSimpleDateFormat {
public static void main(String[] args) {
/*传入日期 进行格式转换*/
Callable<Date> task = ()->DateFormatThredLocal.convert("20181207");
/*创建长度为10的线程池*/
ExecutorService pool = Executors.newFixedThreadPool(10);
/*创建集合*/
ArrayList<Future<Date>> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(pool.submit(task));
}
list.stream().map((f)->{
try {
return f.get();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
})
.forEach(System.out::println);
pool.shutdown();
}
@Test
public void test1() {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
System.out.println(dtf.parse("20181207"));
}
}
使用LocalDate、LocalTime、LocalDateTime
- LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。
package test.date;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.Period;
import java.time.ZoneOffset;
import org.junit.Test;
public class TestLocalDateTime {
@Test
public void test1() {
/*获取当前系统日期时间 并输出*/
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
System.out.println("----------------");
/*指定日期时间 进行输出*/
LocalDateTime of = LocalDateTime.of(2018,10,19,13,22,33);
System.out.println(of);
System.out.println("--------------");
/*在当前日期时间上加两年*/
LocalDateTime plusYears = now.plusYears(2);
System.out.println(plusYears);
System.out.println("----------------");
/*在当前日期时间上减两个月*/
LocalDateTime minusMonths = now.minusMonths(2);
System.out.println(minusMonths);
System.out.println("---------------");
System.out.println(now.getYear());
System.out.println(now.getMonthValue());
System.out.println(now.getDayOfMonth());
System.out.println(now.getHour());
}
/*Instant :时间戳(以Unix元年:1970年1月1日到某个时间的毫秒值)*/
@Test
public void test2() {
Instant now = Instant.now();//默认获取UTC时区 不是本机时间
System.out.println(now);
/*设置偏移量运算*/
OffsetDateTime atOffset = now.atOffset(ZoneOffset.ofHours(8));
System.out.println(atOffset);
/*转成毫秒时间*/
System.out.println(now.toEpochMilli());
}
/*计算两个时间之间的间隔 Duration
* period 计算两个日期之间的间隔
* */
@Test
public void test3() {
Instant now = Instant.now();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant now1 = Instant.now();
Duration between = Duration.between(now, now1);
System.out.println(between.toMillis());
System.out.println("----------------");
LocalTime lt1 = LocalTime.now();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
LocalTime lt2 = LocalTime.now();
Duration d = Duration.between(lt1, lt2);
System.out.println(d.toMillis());
}
@Test
public void test4() {
LocalDate ld1 = LocalDate.of(2015,1,1);
LocalDate ld2 = LocalDate.now();
Period period = Period.between(ld1, ld2);
System.out.println(period);
System.out.println("==============");
System.out.println(period.getYears());
System.out.println(period.getDays());
System.out.println(period.getMonths());
}
}
日期的操纵
-
**TemporalAdjuster:**时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
-
**TemporalAdjusters:**该类通过静态方法提供了大量的常用TemporalAdjuster的实现。
/*例如获取下个周日**/ LocalDate nextSunday = LocalDate.now().with( TemporalAdjusters.next(DayOfWeek.SUNDAY) ); /*格式化DateTimeFormatter:格式化时间/日期*/ DateTimeFormatter dtf2=DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); String str = dtf2.format(LocalDateTime.now()); /*解析回*/ /*按dtf2格式解析日期字符串*/ LocalDateTime newDate = LocalDateTime.now().parse(str,dtf2);
重复注解与类型注解
Java8对注解处理提供了两点改进,可重复的注解及可用于类型的注解。