jdk8学习
学习的思维方式
1、大处着眼,小处着手。
2、逆向思维、反证法。
3、透过问题看本质,比如ArrayList本质还是个数组。
总体结构
1 lambda表达式
2 函数式(Functional)接口
3 方法引用与构造器引用
4 强大的Stream API
5 Optional类
java8新特性
速度更快
代码更少(增加了新的语法:lambda表达式)
强大的Stream API
便于并行
最大化减少空指针异常:Optianal
Nashorn引擎,允许在JVM上运行JS应用
lambda表达式的使用
-> : lambda操作符
语法格式一:不需要参数,不需要返回值。
Runnable r = () -> System.out.println("hello");
r.run();
语法格式二:一个参数,没有返回值。
Consumer<String> cu = (String s) -> System.out.println(s);
cu.accept("吃了吗您内?");
语法格式三:类型推断,可以通过泛型推断出入参类型。
Consumer<String> cu = (s) -> System.out.println(s);
语法格式四:如果参数只有一个,小括号可以省略。
Consumer<String> cu = s -> System.out.println(s);
语法格式五:两个及以上参数,多条执行语句,并且可以有返回值。
Comparator<Integer> ctr = (o1, o2) -> {
System.out.println("第一");
System.out.println("第二");
return o1.compareTo(o2);
};
语法格式六:只有一条执行语句,return与大括号可以省略。
Runnable r = () -> System.out.println("hello");
函数式接口
如果一个接口中,只声明了一个抽象方法,这个接口就称之为函数式接口。
这样的接口会在类的上面添加@FunctionalInterface的注解。
@FunctionalInterface
public interface MyFunctionInterface {
int method();
}
lamdba依赖于函数式接口,它的本质是作为函数式接口的实例。
java内置的4大核心函数式接口
消费型接口
Consumer<T> void accept(T t)
public void happyTime(double money, Consumer<Double> con) {
con.accept(money);
}
// test
happyTime(500, m -> System.out.println("今天吃焖锅,价格为:" + m));
供给型接口
Supplier<T> T get()
函数型接口
Function<T,R> R apply(T t)
断定型接口
Predicate<T> boolean test(T t)
public static List<String> filterString(List<String> list, Predicate<String> pre) {
List<String> filterList = new ArrayList<>();
for (String s : list) {
if (pre.test(s)) {
filterList.add(s);
}
}
return filterList;
}
// test
List<String> list = Arrays.asList("北京","南京","天津","东京");
List<String> res = filterString(list, s -> s.contains("京"));
System.out.println(res);
方法引用
1、使用情景:当要传递给lambda体的操作,已经有实现的方法了,可以使用方法引用。
2、方法引用:本质上就是lambda表达式,而lambda表达式作为函数式接口的实例。所以方法引用,也是函数式接口的实例。
3、使用格式:类(或对象)::方法名
4、具体分为如下的三种情况:
对象::非静态方法
// 情况一:对象::实例方法
// Consumer中的void accept(T t)
// PrintStream中的void println(T t)
Consumer<String> c = System.out::println;
c.accept("北京");
// Supplier中的T get()
// Employee中的String getName()
Employee e = new Employee(1,"Tom",23,6500);
Supplier<String> s = e::getName();
类::静态方法
// 情况二:类::静态方法
Comparator<Integer> cp = Integer::compare;
int res = cp.compare(1, 2);
// Function中的R apply(T t)
// Math中的Long round(Double d)
Function<Double, Long> f = Math::round;
类::非静态方法
// 情况三:类::实例方法
// Comparator中的int compare(T t1, T t2)
// String中int t1.compareTo(t2)
Comparator<String> cp3 = String::compareTo;
5、要求:接口中的抽象方法,形参列表和返回值类型与方法引用的形参列表和返回值类型相同。
构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。抽象方法的返回值类型即为构造器所属的类的类型。
// 无参
Supplier<Employee> s = Employee :: new;
// 一个入参
Function<Integer,Employee> f = Employee :: new;
数组引用
把数组看成一个特殊的类,则写法与构造器引用一致。
Function<Integer,String[]> f = String[] :: new;
Stream API
1、Stream关注的是对数据的运算,与CPU打交道。
集合关注的是数据的存储,与内存打交道。
2、1)Stream自己不会存储元素。2)Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。3)Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
3、Stream执行流程
1)Stream的实例化
2)一系列的中间操作(过滤、映射...)
3)终止操作
4、说明:
一个中间操作链,对数据源的数据进行处理。
创建Stream的四种方式
// 方式一:通过集合
List<Cat> cats = Cat.getCats();
// 返回一个顺序流
Stream<Cat> stream = cats.stream();
// 返回一个并行流
Stream<Cat> parallelStream = cats.parallelStream();
// 方式二:通过数组
int[] arr = {1, 2, 3};
IntStream intStream = Arrays.stream(arr);
// 方式三:通过Stream的of()
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
// 方式四:创建无限流
// 遍历前10个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
// 生成10个随机数
Stream.generate(Math::random).limit(10).forEach(System.out::println);
Stream的中间操作
1、筛选与切片
方法 | 描述 |
---|---|
filter(Predicate p) | 接收Lambda,从流中排除某些元素。 |
distinct() | 筛选,通过流所生成元素的hashCode()和equals()去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补 |
List<Cat> cats = Cat.getCats();
// 查询猫的大小,大于等于2的信息
// filter
cats.stream().filter(c -> c.getSize() >= 2).forEach(System.out::println);
// 截断流 limit
cats.stream().limit(2).forEach(System.out::println);
// 跳过元素 skip
cats.stream().skip(1).forEach(System.out::println);
// 筛选 distinct 去除重复数据,按照类中的equals和hashcode
cats.stream().distinct().forEach(System.out::println);
2、映射
方法 | 描述 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
// map
List<String> letters = Arrays.asList("aa", "bb", "cc", "dd");
letters.stream().map(s -> s.toUpperCase(Locale.ROOT)).forEach(System.out::println);
// 练习:获取员工姓名长度大于3的员工的姓名
List<Employee> employees = EmployeeData.getEmployees;
Stream<String> namesStream = employees.stream().map(Employee::getName);
namesStream.filter(name -> name.length() > 3).forEach(System.out::println);
// 集合里面套集合的时候想要遍历每个元素,可以使用flatMap,这样将list套list全打开为单个
3、排序
方法 | 描述 |
---|---|
sorted() | 产生一个新流,其中按照自然顺序排序 |
sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
Stream的终止操作
1、匹配与查找
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用Collection接口需要用户去做迭代,称为外部迭代。相反,Stream API使用内部迭代,它帮你把迭代做了) |
// 是否所有员工的年龄都大于18岁
boolean isAllMatch = employees.stream().allMatch(e -> e.getAge() > 18);
// 是否存在员工的工资大于10000
boolean isAnyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
// 是否存在员工姓“雷”
boolean isNoneMatch = employees.stream().noneMatch(e -> e.getName().contains("雷"));
// 查找第一个元素
Optional<Employee> e = employees.stream().findFirst();
// 查找任意一个元素
Optional<Employee> e = employees.stream().findAny();
// 计算工资大于5000的员工的个数
long c = employees.stream().filter(e -> e.getSalary() > 5000).count();
// 查询最大的工资
Stream<Double> salary = employees.stream().map(e -> e.getSalary());
Optional<Double> maxSalary = salary.max(Double::compare);
// 查询工资最低的员工
Optional<Employee> minSalaryEmployee = employees.stream().min((e1,e2) -> Double.compare(e1.getSalary, e2.getSalary)));
// 内部迭代
employees.stream().forEach(System.out::println);
2、归约
方法 | 描述 |
---|---|
reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回Optional |
// 计算1~10的自然数的和
List<Integer> num = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer reduce = num.stream().reduce(0, Integer::sum);
System.out.println(reduce);
// 计算公司所有员工的工资总和
List<Employee> es = employeeData.getEmployees();
Stream<Double> salary = employees.stream().map(Employee::getSalary);
Option<Double> sum = salary.reduce(Double::sum);
3、收集
方法 | 描述 |
---|---|
Collect(Collector c) | 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。 |
// 查找工资大于6000的员工,结果返回为一个List或Set
List<Employee> es = employeeData.getEmployees();
List<Employee> re = es.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
Optional类
创建Optional类对象的方法:
Optional.of(T t) : 创建一个Optional实例,t必须非空。
Optional.empty() : 创建一个空的Optianal实例。
Optional.ofNullable(T t) : t可以为null。
判断Optional容器中是否包含对象:
boolean isPresent():判断是否包含对象
void ifPresent(Consumer<? super T> consumer):如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。
获取Optional容器的对象:
T get():如果调用对象包含值,返回该值,否则抛异常。
T orElse(T other):如果有值则将其返回,否则返回指定的other对象。
T orElseGet(Supplier<? extends T> other):如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
T orElseThrow(Supplier<? extends X> exceptionSupplier):如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。