java8新特性
java 版本可能早已过了java8了,但是lambda表达式的运用在项目中可能变的越来越多了,所有特地整理一下。
lambda 表达式
lambda是一个匿名内部类,我们可以把lambda 表达式理解成一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁,灵活的代码。作为一种更紧凑的代码风格,使java的语言表达能力得到提升。
基础语法格式
无参,无返回值
Runnable runnable = () -> System.out.println("new Runnable");
runnable.run();
有参,无返回值
Consumer<String> consumer = (x) -> System.out.println(x);
Consumer<String> consumer = x -> System.out.println(x); // 括号 可以不写
consumer.accept("test");
有参,有返回值
Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
Comparator<Integer> comparator = (Integer x,Integer y) -> Integer.compare(x,y); // 因为类型推断,可以省。
// 当有多行语句时
Comparator<Integer> comparator = (x,y) -> {
System.out.println(x);
return Integer.compare(x,y);
};
表达式需要“函数式接口”的支持
函数式接口:接口中只有一个抽象方法的接口,可以使用 @FunctionalInterface 检查是否是函数式接口。
// 函数式接口
@FunctionalInterface
public interface MyFun {
public Integer getValue(Integer num);
}
Integer operation = operation(100, (x) -> x * x);
//等价于
MyFun myFun = new MyFun() {
@Override
public Integer getValue(Integer num) {
return num * num;
}
};
System.out.println(operation);
四大内置核心函数式接口
上面定义MyFun 函数式接口的时候,深深感受到为了写而写。
其实java8 给我们提供了一些接口,以下是一些用法
/**
*
* Consumer<T> : 消费型接口
* void accept(T t);
*
* Supplier<T> : 供给型接口
* T get();
*
* Function<T,R> : 函数型接口
* R apply(T t);
*
* Predicate<T> : 断言型接口
* boolean test(T t);
*
*/
public class Test {
public static void main(String[] args) {
// Consumer
consumer(10000, (m) -> System.out.println("用了"+m + "yuan"));
// Supplier
List<Integer> supplier = supplier(10, () -> (int) (Math.random() * 100));
supplier.stream().forEach(System.out::println);
// Function
String function = function("123123123", (x) -> x.substring(0, 5));
System.out.println(function);
// Predicate
List<String> list = Arrays.asList("123", "1234", "12345", "123456", "1234567");
List<String> predicate = predicate(list, (x) -> x.length() > 4);
predicate.stream().forEach(System.out::println);
}
// Consumer
public static void consumer(double money , Consumer<Double> son){
son.accept(money);
}
// Supplier
public static List<Integer> supplier(int num , Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for(int i = 0 ; i < num ; i++){
Integer n = sup.get(); // get 方法就是 -> 后面的实现(int) (Math.random() * 100)
list.add(n);
}
return list;
}
//Function
public static String function(String str, Function<String,String> function){
return function.apply(str);
}
// Predicate
public static List<String> predicate(List<String> strList , Predicate<String> newStr){
List<String> list = new ArrayList<>();
for (String str : strList){
if(newStr.test(str)){
list.add(str);
}
}
return list;
}
}
方法引用和构造器引用
参考编码注解。
import org.junit.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.*;
/**
* 方法引用 : 若 Lambda 体中的内容有方法已经实现了,我们可以使用“方法引用”
* (可以理解为方法引用是Lambda表达式的另一种表达方式)
*
* 主要有三种语法方式:
*
* 对象::实例方法名
*
* 类::静态方法
*
* 类::实例方法名
*
* 构造器引用
*
* ClassName::new
* 注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致
*
* 数组引用:
* Type[]::new;
*/
public class TestMethodRef {
//方法引用
@Test
public void test1(){
// 1
PrintStream out = System.out;
Consumer<String> con = (x) -> out.print(x);
//等价于
Consumer<String> tConsumer = out::print;
//2
Comparator<Integer> con2 = (x, y) -> Integer.compare(x,y);
//等价于
Comparator<Integer> compare = Integer::compare;
// compare.compare(x,y);
//3
BiPredicate<String,String> biPredicate = (x,y) -> x.equals(y);
// 等价于
BiFunction<String, Object, Boolean> stringObjectBooleanBiFunction = String::equals;
}
//构造器引用
@Test
public void test2(){
Supplier<Student> supplier= () -> new Student();
// 等价于
Supplier<Student> supplier2 = Student::new;
Function<String , Student> su = (x) -> new Student(x);
//等价于
Function<String , Student> supplier3 = Student::new;
// Student student = supplier3.apply("123"); // 查看用了哪个构造方法,其实和Function<String , Student> 有关
//数组引用
Function<Integer , String[]> fun = (x) -> new String[x];
//等价于
Function<Integer, String[]> runnable = String[]::new;
}
}
Stream 流
流的创建
/**
* 一:Stream 的三个操作步骤
*
* 1,创建Stream
*
* 2,执行Stream
*
* 3,终止操作(终端操作)
*
*/
public class TestStreeam {
// 创建流
@Test
public void test(){
// 集合
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
// 数组
Student[] students = new Student[10];
Stream<Student> stream1 = Arrays.stream(students);
// 静态方法
Stream<String> stringStream = Stream.of("", "", "");
//创建无限流
Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2);
iterate.forEach((x) -> System.out.print(x));// 无限打印
iterate.limit(20).forEach(System.out::print);// 用limit限制数量
}
流的操作
filter
@Test
public void test2(){
List<Student> list = new ArrayList<>();
for (int i =0 ; i < 10 ; i++){
list.add(new Student(String.valueOf(i)));
}
// 过滤 filter
list.stream().filter((e) -> Integer.valueOf(e.name) > 3).forEach(System.out::print);
list.stream().filter((e) -> Integer.valueOf(e.name) > 3)
.distinct() // 去重,要实现equals 和 hashCode
.forEach(System.out::print);
}
map
@Test
public void test3(){
// map 将元素转换成其他形式或提取信息
List<Student> list = new ArrayList<>();
for (int i =0 ; i < 10 ; i++){
list.add(new Student(String.valueOf(i)));
}
list.stream().map((e) -> { e.setAge(21);
return e;
}).forEach(System.out::print);
}
flatMap
对Stream<Stream> 的对象操作时,就用flatMap操作。 最后会把所有的T 取出来。
sorted(Comparator)
@Test
public void test4(){
List<String> list = Arrays.asList("AAA", "CCC", "BBB", "EEE", "ZZZ");
list.stream().sorted().forEach(System.out::print); // 排序
//或者用
List<Student> list2 = new ArrayList<>();
for (int i =0 ; i < 10 ; i++){
list2.add(new Student(String.valueOf(i),i+1));
}
list2.stream().sorted((e1,e2) -> {
if (e1.getAge().equals(e2.getAge())){
return e1.getName().compareTo(e1.getName());
}else{
return e1.getAge().compareTo(e2.getAge());
}
}).forEach(System.out::print);
}
终止操作
//终止操作
@Test
public void test5(){
List<Student> list = new ArrayList<>();
for (int i =0 ; i < 10 ; i++){
list.add(new Student(String.valueOf(i),i+1));
}
boolean b1 = list.stream().allMatch((e) -> e.getAge() == 1); // 全匹配
boolean b2 = list.stream().anyMatch((e) -> e.getAge() == 1);
boolean b3 = list.stream().noneMatch((e) -> e.getAge() == 1);
Optional<Student> first = list.stream()
.sorted((e1, e2) -> e1.getAge().compareTo(e2.getAge()))
.findFirst(); // 找到第一个
Student student = first.get();
Optional<Student> free = list.parallelStream() // 并行流
.sorted((e1, e2) -> e1.getAge().compareTo(e2.getAge()))
.findAny(); // 找到第一个
Student student2 = first.get();
long count = list.stream().count();
Optional<Student> max = list.stream().max((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()));
Student student1 = max.get();
Optional<Integer> min = list.stream().map(Student::getAge).min(Double::compare);
Integer integer = min.get();
}
reduce 规约
/**
* 规约
* reduce(T identity , BinaryOperator) / reduce(BinaryOperator)
* 可以将流中元素反复结合起来,得到一个值
*/
@Test
public void test6(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//计算
Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
//集合map 使用
list.stream().map(Double::valueOf).reduce(Double::sum);
}
collect
/**
* 收集
* collect
* 将流转换成其他形式,接收一个Collector 接口的实现,用于给Stream中元素做汇总的方法
*
*/
@Test
public void test7(){
List<Student> list = new ArrayList<>();
for (int i =0 ; i < 10 ; i++){
list.add(new Student(String.valueOf(i),i+1));
}
/**
* collect(Collector c)
* 将流转换成其他形式。接收一个Collector接口的实现,用于给Stream 中元素做汇总的方法。
*
* Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到List,Set,Map)。 但是Collector
* 实用类提供了很多静态方法,可以方便地创建常见收集器实例
*/
// list
List<Integer> collect = list.stream().map(Student::getAge).collect(Collectors.toList());
// set
Set<Integer> collect1 = list.stream().map(Student::getAge).collect(Collectors.toSet());
HashSet<Integer> collect2 = list.stream().map(Student::getAge).collect(Collectors.toCollection(HashSet::new));
//总数
list.stream().collect(Collectors.counting());
//平均值 / 总和
Double collect3 = list.stream().collect(Collectors.averagingInt(Student::getAge));
// 最大值
Optional<Student> max = list.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getAge(), e2.getAge())));
System.out.println(max.get());
// 分组
Map<String, List<Student>> map = list.stream().collect(Collectors.groupingBy(Student::getName));
// 多级分组
Map<Integer, Map<Integer, String>> collect4 = list.stream().collect(Collectors.groupingBy(Student::getAge, toMap(Student::getAge, Student::getName)));
// 也可以写成这样
Map<Integer, Map<Integer, String>> collect5 = list.stream().collect(Collectors.groupingBy(e -> e.getAge(), toMap(Student::getAge, Student::getName)));
// 分区
Map<Boolean, List<Student>> collect6 = list.stream().collect(Collectors.partitioningBy(e -> e.getAge() > 2));
System.out.println(collect6);
DoubleSummaryStatistics collect7 = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
collect7.getAverage();
collect7.getMax();
// ...
//连接字符串
String collect8 = list.stream().map(Student::getName).collect(Collectors.joining(","));
System.out.println(collect8); // 0,1,2,3,4,5,6,7,8,9 //发现末尾的逗号都给你去掉了
String collect9 = list.stream().map(Student::getName).collect(Collectors.joining(",", "前缀", "后缀"));
System.out.println(collect9);
}
并行流和串行流
略 (后面有时间写)
Optional 类
是一个容器类,代表一个值存在或不存在,原来用null表示一个值存在,现在用Optional 可以更好的表达这个概念,并且可以避免空指针。
@Test
public void test(){
//创建一个Optional实例,如果是空指针异常,则当前行报错
Optional<Student> op = Optional.of(new Student());
Student student = op.get();
// 构建一个空的
Optional<Student> empty = Optional.empty();
// 也会报错,因为 ofNullable 的实现就是 return value == null ? empty() : of(value);
Optional<Student> student1 = Optional.ofNullable(new Student());
// 如果有值,才获取
if(student1.isPresent()){
Student student2 = student1.get();
}
// 有值则op中原先的值,没值则用 new Student("zs", 1)
Student student2 = op.orElse(new Student("zs", 1));
// 和上面一样,只是内部用函数式接口,更方便处理。
op.orElseGet(() -> new Student("zs", 1));
Optional<Student> zs = Optional.ofNullable(new Student("zs", 1));
// map
Optional<String> s = zs.map((e) -> e.getName());
// flatMap
Optional<String> s1 = zs.flatMap((e) -> Optional.of(e.getName()));
}
//实战中使用,对象初始化必须加上Optional.empty();
private Optional<Student> student = Optional.empty();
接口中可以有默认方法和静态方法
public interface myInterface {
// 接口中要实现默认方法,需要在方法前添加 default 关键字
default void test(){
System.out.println("---");
}
// 静态方法
public static void test2(){
System.out.println("---");
}
}