1.lambda & stream
什么是 Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。并行(parallel)程序
parallelStream 是流并行处理程序的代替方法。就是 stream的并发方法。limit
limit 方法用于获取指定数量的流。
public class User implements Comparable<User>{
private long id;
private String name;
private int age;
public long getId() {
return id;
}
public void setId(long 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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(User user) {
int num = this.age - user.getAge();
if(num > 0){
return 1;
}else if(num == 0){
return 0;
}
return -1;
}
}
1.1 遍历
@Test
public void jdk8_1(){
List<String> list = new ArrayList<String>();
list.add("2");
list.add("1");
list.add("3");
Collections.sort(list); //排序
//直接循环输出
for (String str :list){
System.out.print(" " + str);
}
System.out.println();
//采用lambda 表达式遍历
list.forEach(item -> {
System.out.print(" " + item);
});
//stream流遍历
System.out.println();
list.stream().forEach(item -> {
System.out.print(" " + item);
});
}
1.2 排序
使用普通排序 要是实现Comparable
或者使用匿名内部类
@Test
public void jdk8_2(){
List<User> list = initList();
/**
* 1.使用的这个方法 User对象必须实现Comparable 接口
*/
Collections.sort(list);
/**
* 2.这种方式不需要实现Comparable 接口
*/
Collections.sort(list, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() - o2.getAge();
}
});
/**
* 3.使用lambda表达式 或者stream
*/
list.stream().sorted(Comparator.comparing(User::getAge));
Collections.sort(list, Comparator.comparing(User::getAge));
list.forEach(item -> {
System.out.println(item.toString());
});
}
1.3 list转map
@Test
public void jdk8_3(){
List<User> list = initList();
//1.传统遍历 不做介绍
//2.lambda id:Object
Map<Long, User> userMap = list.stream().collect(Collectors.toMap(user -> user.getId(), user -> user));
userMap.entrySet().forEach(entry ->{
System.out.println(entry.getKey() + " : " + entry.getValue().toString());
});
userMap.values().forEach(value ->{
System.out.println(value.toString());
});
userMap.keySet().forEach(key ->{
System.out.println(userMap.get(key).toString());
});
/**
* 只保留list的某些字段
*/
List<Long> ids = list.stream().map(User::getId).collect(Collectors.toList());
System.out.println(ids);
/**
* list转map 并分组
*/
Map<Integer, List<User>> groupMap = list.stream().collect(Collectors.groupingBy(user -> user.getAge()));
System.out.println(groupMap.values());
}
1.4 求和过滤
@Test
public void jdk8_4(){
List<User> list = initList();
int sumAge = list.stream().collect(Collectors.summingInt(User::getAge));
System.out.println(sumAge);
//过滤
List<User> list2 = list.stream().filter(user -> {
return user.getAge() == 11;
}).collect(Collectors.toList());
System.out.println(list2);
}
1.5 统计
@Test
public void jdk8_5(){
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt(item -> item).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
}
2.optional
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
1 | static 返回空的 Optional 实例。 |
---|---|
2 | boolean equals(Object obj) 判断其他对象是否等于 Optional。 |
3 | Optional 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。 |
4 | T get() 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException |
5 | void ifPresent(Consumer<? super T> consumer) 如果值存在则使用该值调用 consumer , 否则不做任何事情。 |
6 | boolean isPresent() 如果值存在则方法会返回true,否则返回 false。 |
7 | static 返回一个指定非null值的Optional。 |
8 | static 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。 |
9 | T orElse(T other) 如果存在该值,返回值, 否则返回 other。 |
@Test
public void jdk8_4(){
User user1 = null;
User user2 = new User(1L, "张三", 12);
Optional<User> userOptional1 = Optional.ofNullable(user1);
Optional<User> userOptional2 = Optional.of(user2);
System.out.println(userOptional2.isPresent());
User user = userOptional2.get();
}
3.方法引用
方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号 :: 。
/**
* @author meikb
* @desc *
* @date 2020-05-11 14:18
*/
public class Fun_ref {
@Test
public void fun_1(){
List<Car> cars = new ArrayList<>();
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
cars.add(car1);
cars.add(car2);
cars.add(car3);
cars.add(Car.create(Car::new));
cars.forEach(Car::run);
}
}
class Car {
public void run(){
System.out.println("run****************");
}
public static Car create(final Supplier<Car> supplier) {
return supplier.get();
}
}
public class Fun_ref {
@Test
public void fun_1(){
List<Car> cars = new ArrayList<>();
Car car = new Car();
Window win = new Window();
car.run(Window::process);
}
}
class Car {
public Car(){
System.out.println("hi");
}
public Object run(final Supplier<Object> supplier){
try {
System.out.println("lock");
return supplier.get();
}finally {
System.out.println("unlock");
}
}
}
class Window{
public static Object process(){
System.out.println("process");
return new Object();
}
}
输出:
hi
lock
process
unlock
就是说 car.run(Window::process); 这个段代码在执行get的的时候才会 执行Window 的 process 方法
4.函数式接口
1 | BiConsumer |
---|---|
2 | BiFunction |
3 | BinaryOperator |
4 | BiPredicate |
5 | BooleanSupplier代表了boolean值结果的提供方 |
6 | Consumer |
7 | DoubleBinaryOperator代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。 |
8 | DoubleConsumer代表一个接受double值参数的操作,并且不返回结果。 |
9 | DoubleFunction |
10 | DoublePredicate代表一个拥有double值参数的boolean值方法 |
11 | DoubleSupplier代表一个double值结构的提供方 |
12 | DoubleToIntFunction接受一个double类型输入,返回一个int类型结果。 |
13 | DoubleToLongFunction接受一个double类型输入,返回一个long类型结果 |
14 | DoubleUnaryOperator接受一个参数同为类型double,返回值类型也为double 。 |
15 | Function |
16 | IntBinaryOperator接受两个参数同为类型int,返回值类型也为int 。 |
17 | IntConsumer接受一个int类型的输入参数,无返回值 。 |
18 | IntFunction |
19 | IntPredicate:接受一个int输入参数,返回一个布尔值的结果。 |
20 | IntSupplier无参数,返回一个int类型结果。 |
21 | IntToDoubleFunction接受一个int类型输入,返回一个double类型结果 。 |
22 | IntToLongFunction接受一个int类型输入,返回一个long类型结果。 |
23 | IntUnaryOperator接受一个参数同为类型int,返回值类型也为int 。 |
24 | LongBinaryOperator接受两个参数同为类型long,返回值类型也为long。 |
25 | LongConsumer接受一个long类型的输入参数,无返回值。 |
26 | LongFunction |
27 | LongPredicateR接受一个long输入参数,返回一个布尔值类型结果。 |
28 | LongSupplier无参数,返回一个结果long类型的值。 |
29 | LongToDoubleFunction接受一个long类型输入,返回一个double类型结果。 |
30 | LongToIntFunction接受一个long类型输入,返回一个int类型结果。 |
31 | LongUnaryOperator接受一个参数同为类型long,返回值类型也为long。 |
32 | ObjDoubleConsumer |
33 | ObjIntConsumer |
34 | ObjLongConsumer |
35 | Predicate |
36 | Supplier |
37 | ToDoubleBiFunction |
38 | ToDoubleFunction |
39 | ToIntBiFunction |
40 | ToIntFunction |
41 | ToLongBiFunction |
42 | ToLongFunction |
43 | UnaryOperator |
public class JDK8_Fun {
@Test
public void fun_1(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// Predicate<Integer> predicate = n -> true
// n 是一个参数传递到 Predicate 接口的 test 方法
// n 如果存在则 test 方法返回 true
System.out.println("输出所有数据:");
// 传递参数 n
eval(list, n->true);
// Predicate<Integer> predicate1 = n -> n%2 == 0
// n 是一个参数传递到 Predicate 接口的 test 方法
// 如果 n%2 为 0 test 方法返回 true
System.out.println("输出所有偶数:");
eval(list, n-> n%2 == 0 );
// Predicate<Integer> predicate2 = n -> n > 3
// n 是一个参数传递到 Predicate 接口的 test 方法
// 如果 n 大于 3 test 方法返回 true
System.out.println("输出大于 3 的所有数字:");
eval(list, n-> n > 3 );
}
public void eval(List<Integer> list, Predicate<Integer> predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
System.out.print(n + " ");
}
}
System.out.println();
}
}
5.默认方法
接口内部定义方法 使用default
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}
6.新日期
Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
- 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
- 设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
- 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
- Local(本地) − 简化了日期时间的处理,没有时区的问题。
Zoned(时区) − 通过制定的时区处理日期时间。
```java @Test public void testLocalDateTime(){// 获取当前的日期时间 LocalDateTime currentTime = LocalDateTime.now(); System.out.println(“当前时间: “ + currentTime); //当前时间: 2020-05-11T14:53:10.011
LocalDate date1 = currentTime.toLocalDate(); System.out.println(“date1: “ + date1); //date1: 2020-05-11
Month month = currentTime.getMonth(); int day = currentTime.getDayOfMonth(); int seconds = currentTime.getSecond();
//月: MAY, 日: 11, 秒: 10 System.out.println(“月: “ + month +”, 日: “ + day +”, 秒: “ + seconds);
LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
System.out.println("date2: " + date2); //2012-05-10T14:53:10.011
// 12 december 2014
LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);
System.out.println("date3: " + date3); //2014-12-12
// 22 小时 15 分钟
LocalTime date4 = LocalTime.of(22, 15);
System.out.println("date4: " + date4); //22:15
// 解析字符串
LocalTime date5 = LocalTime.parse("20:15:30");
System.out.println("date5: " + date5); //20:15:30
}
输出:
```java
当前时间: 2020-05-11T14:53:10.011
date1: 2020-05-11
月: MAY, 日: 11, 秒: 10
date2: 2012-05-10T14:53:10.011
date3: 2014-12-12
date4:
date5:
7.fork/join
fork/join作为一个并发框架在jdk7的时候就加入到了我们的java并发包java.util.concurrent中,并且在java 8 的lambda并行流中充当着底层框架的角色。
fork/join大体的执行过程,先把一个大任务分解(fork)成许多个独立的小任务,然后起多线程并行去处理这些小任务。处理完得到结果后再进行合并(join)就得到我们的最终结果。