java8特性
Java8 新增了非常多的特性,我们主要讨论以下几个:
- Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
- 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
- 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
- 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
- Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
- Date Time API − 加强对日期与时间的处理。
- Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
- Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。
Lambda
lambda是一个匿名函数,可以理解为Lambda表达式是一段可以传递的代码
匿名实现类的方式实现的功能都能通过lambda表达式实现
格式:
->
lambda操作符->
左边: lambda形参列表, 就是接口中抽象方法的形参列表->
右边: lambda体, 就是重写抽象方法的方法体
//无参数,无返回值
@Test
public void test03(){
Runnable r1 = () -> System.out.println("r1 run");
r1.run();
}
//有参数,无返回值
@Test
public void test04(){
Consumer<Integer> con = x -> System.out.println(x);
con.accept(5);
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
}
//有参数,有返回值
@Test
public void test05(){
Comparator<Integer> com = (a, b) -> a.compareTo(b);
System.out.println(com.compare(1,5));
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
}
Functional
函数式接口, 可以通过lambda表达式创建函数式接口对象
可以在接口上使用@FunctionalInterface
注解,这样可以检验这是否是一个函数式接口
内置函数式接口
函数式接口的实例化可以通过lambda表达式来实现
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer<T>消费型接口 | T | void | 对T类型对象进行应用操作 |
Supplier<T>供给型接口 | 无 | T | 返回类型为T, T get() |
Function<T,R>函数型接口 | T | R | 对T类型对象应用操作,返回R类型结果,R apply(T t) |
Predicate<T>断言型接口 | T | boolean | 类型为T的对象是否满足某约束,返回boolean test(T t) |
示例:
//lambda用法 过滤list<String>值为"v1"的项
@Test
public void lambdaTest(){
List<String> list = Arrays.asList("v1", "v2", "v3");
List<String> aftList = filterString(list, s -> s.equals("v1"));
System.out.println(aftList); //[v1]
}
public List<String> filterString(List<String> list, Predicate<String> pre){
ArrayList<String> filterList = new ArrayList<>();
for (String s : list) {
if (pre.test(s)) filterList.add(s);
}
return filterList;
}
方法引用
- 当要传递给lambda的操作已经有实现方法了, 可以使用方法引用
Consumer<String> con = str -> System.out.println(str);
con.accept("ss");
//lambda中仅打印str值 , System.out中已经有打印方法, 可以直接使用方法引用
//情况1: 对象 :: 实例方法
//Consumer 中的 void accept(T t)方法
//PrintStream中的 void println(T t)
Consumer<String> con2 = System.out::println;
con2.accept("ss");
- 方法引用可以看作lambda的深层表达
要求:
- 接口中的抽象方法的形参列表和返回值类型和方法引用的方法类型相同
对象 :: 实例方法
//情况1: 对象 :: 实例方法
//Consumer 中的 void accept(T t)方法
//PrintStream中的 void println(T t)
Consumer<String> con2 = System.out::println;
con2.accept("ss");
//Supplier 中的 T get()
//User 中的 String getName()
//普通lambda
User user = new User(1,"tom",20);
Supplier<String> sup = () -> user.getName();
System.out.println(sup.get());
//方法引用
Supplier<String> sup = emp::getName;
System.out.println(sup.get());
类 :: 静态方法
//情况二: 类 :: 静态方法
//Comparator中的 int compare(T t1, T t2)
//Integer中的 int compare(T t1, T t2)
Comparator<Integer> com = (t1,t2) -> Integer.compare(t1,t2);
System.out.println(com.compare(1,3)); //-1
Comparator<Integer> comp01 = Integer::compareTo;
System.out.println(comp01.compare(1,3)); //-1
//Function 中的 R apply(T t)
//Math 中的 Long round(Double d) round为将传入浮点数小数部分四舍五入取整
Function<Double,Long> fun = d -> Math.round(d);
System.out.println(fun.apply(2.6)); //3
Function<Double,Long> fun1 = Math::round;
System.out.println(fun1.apply(2.0)); //2
类 :: 实例方法
//情况三: 类 :: 实例方法
//Comparator中的 int compare(T t1, T t2)
//Integer 中的 int t1.compareTo(t2)
Comparator<Integer> comparator = (t1,t2) -> t1.compareTo(t2);
System.out.println(comparator.compare(1,3));
Comparator<Integer> comparator = Integer::compareTo;
System.out.println(comparator.compare(1,3));
//BiPredicate中的boolean test(T t1, T t2)
//String 中的 boolean t1.equals(t2)
BiPredicate<String,String> pre = (s1,s2) ->s1.equals(s2);
System.out.println(pre.test("a","a"));
BiPredicate<String,String> pre = String::equals;
System.out.println(pre.test("a","a"));
构造器引用
它的语法是Class::new,或者更一般的Class< T >::new实例如下:
final Car car = Car.create( Car::new );
//Supplier 中的 T get()
//Car 的空参构造器
@Test
public void test06(){
Supplier<Car> car = Car::new;
}
//BiFunction 中的 R apply(T t, U u)
@Test
public void test(){
BiFunction<Integer,String,Car> fun = Car::new;
System.out.println(fun.apply(22,"name"));
}
//数组引用
//Function 中的 R apply(T t)
@Test
public void test07(){
Function<Integer,String[]> fun = String[]::new;
String[] arr = fun.apply(5);
}
Stream
- Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对
Java集合
运算和表达的高阶抽象。 - 这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
- 元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
Stream和Collection的区别
- Collection是一种静态的内存数据结构,Stream是有关计算的
java.io | java.util.stream | 用途 |
---|---|---|
存储 | 顺序读写的byte 或char | 顺序输出的任意Java对象实例 |
用途 | 序列化至文件或网络 | 内存计算/业务逻辑 |
Stream的操作三个步骤
- 创建Stream
一个数据源(如:集合、数组),获取一个流
- 中间操作
一个中间操作链,对数据源的数据进行处理
- 终止操作
一旦执行终止操作,就执行中间操作链
,并产生结果。
创建Stream
在 Java 8 中, 集合接口有两个方法来生成流:
- stream() − 为集合创建串行流。
- parallelStream() − 为集合创建并行流。
对基本数据类型的支持:
- 因为Java的范型不支持基本类型,所以我们无法用
Stream<int>
这样的类型,会发生编译错误。为了保存int
,只能使用Stream<Integer>
,但这样会产生频繁的装箱、拆箱操作。为了提高效率,Java标准库提供了IntStream
、LongStream
和DoubleStream
这三种使用基本类型的Stream
,它们的使用方法和范型Stream
没有大的区别,设计这三个Stream
的目的是提高运行效率:
// 将int[]数组变为IntStream:
IntStream is = Arrays.stream(new int[] { 1, 2, 3 });
// 将Stream<String>转换为LongStream:
LongStream ls = List.of("1", "2", "3").stream().mapToLong(Long::parseLong);
//通过集合创建Stream
List<Integer> list= new ArrayList<>();
//返回一个串行流
Stream<Integer> stream = list.stream();
//返回一个并行流
Stream<Integer> parallelStream = list.parallelStream();
//通过数组创建
int[] arr = new int[]{1,2,3,4,5};
IntStream arrStream = Arrays.stream(arr);
//通过Stream的of()
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
//创建无线流
//遍历前10个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
//生成随机数
Stream.generate(Math::random).limit(10).forEach(System.out::println);
Stream的中间操作
筛选和切片
方法 | 描述 |
---|---|
filter(Predicate p) | 接收Lambda (Predicate) ,从流中排除某些元素 |
distinct() | 筛选,通过流所生成元素的hashCode()和equals() 去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补 |
List<person> list = new ArrayList<>();
list.add(new person("aa",3));
list.add(new person("bb",1));
list.add(new person("cc",5));
Stream<person> stream = list.stream();
stream.filter(p -> p.getAge() > 3).forEach(System.out::println); //filter示例
stream.limit(2).forEach(System.out::println); //limit示例
stream.skip(2).forEach(System.out::println); //skip示例
stream.distinct().forEach(System.out::println); //distinct示例
映射
map(Function f)
较为常用
方法 | 描述 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream。 |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream。 |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream。 |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(String::toUpperCase).forEach(System.out::println); //map示例
List<person> list = new ArrayList<>();
list.add(new person("aaaa",3));
list.add(new person("bb",1));
list.add(new person("cccc",5));
//map示例 获取name长度大于3的person的姓名
list.stream().filter(p -> p.getName().length() > 3).map(person::getName).forEach(System.out::println); //aaaa cccc
//map和flatMap区别演示
@Test
public void test(){
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
Stream<Stream<Character>> mapStream = list.stream().map(StreamTest::transStringToStream); //map返回一个stream的stream
mapStream.forEach(s -> s.forEach(System.out::println));
Stream<Character> flatMapStream = list.stream().flatMap(StreamTest::transStringToStream); //flatMap会将内层stream拆开,连接成一个流
flatMapStream.forEach(System.out::println);
}
public static Stream<Character> transStringToStream(String s){
ArrayList<Character> list = new ArrayList<>();
for(Character c : s.toCharArray()){
list.add(c);
}
return list.stream();
}
排序
方法 | 描述 |
---|---|
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
List<String> list = Arrays.asList("aa", "cc", "a", "d", "c");
//需要排序对象实现comparable接口
list.stream().sorted().forEach(System.out::println); //a aa c cc sorted()示例
List<person> list = new ArrayList<>();
list.add(new person("aaaa",3));
list.add(new person("bb",1));
list.add(new person("cc",5));
list.stream().sorted((p1,p2) -> p1.getAge()-p2.getAge()).forEach(System.out::println); //sorted(Comparator com) 示例
//list.stream().sorted(Comparator.comparingInt(person::getAge)).forEach(System.out::println); //可简写为方法引用
Stream的终止操作
匹配与查找
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素(需要在并行流中) , 顺序流中的findAny是取第一个 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代 (使用Collection接口需要用户去做迭代称为外部迭代 。相反,Stream API使用内部迭代——它帮你把迭代做了) |
List<person> list = new ArrayList<>();
list.add(new person("aaaa",3));
list.add(new person("bb",1));
list.add(new person("cc",5));
list.stream().allMatch(p -> p.getAge() > 1); //false allMatch
list.stream().anyMatch(p -> p.getAge() == 5); //true anyMatch
list.stream().noneMatch(p -> p.getAge() > 6); //true noneMatch
list.stream().findFirst(); // Optional[person{name='aaaa', age=3}] findFirst
Optional<person> person = list.stream().findAny(); // Optional[person{name='aaaa', age=3}] findAny
list.stream().count(); // 3
list.stream().max((p1, p2) -> p1.getAge() - p2.getAge()); // Optional[person{name='cc', age=5}] max
list.stream().min((p1, p2) -> p1.getAge() - p2.getAge()); // Optional[person{name='bb', age=1}] min
归约
方法 | 描述 |
---|---|
reduce(T identity, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回Optional<T> |
List<Integer> list = Arrays.asList(1,2,3,4,5);
Integer reduce = list.stream().reduce(2, Integer::sum); //17 reduce
Optional<Integer> opReduce = list.stream().reduce(Integer::sum); //Optional[15] reduce
opReduce.get();
将配置文件的每一行配置通过map()
和reduce()
操作聚合成一个Map<String, String>
List<String> props = List.of("profile=native", "debug=true");
Map<String, String> map = props.stream().map(kv -> {
String[] ss = kv.split("=", 2);
return Map.of(ss[0], ss[1]);
}).reduce(new HashMap<String, String>(), (m, kv) -> {
m.putAll(kv);
return m;
});
收集
方法 | 描述 |
---|---|
collect(Collector c) | 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法 |
- Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)。
List<person> list = new ArrayList<>();
list.add(new person("bb",1));
list.add(new person("cc",5));
//toSet
Set<person> set = list.stream().collect(Collectors.toSet()); //Collectors.toSet() 是一个Collector实例
//[person{name='bb', age=1}, person{name='cc', age=5}] collect
//toMap
Map<String, Integer> map = list.stream().collect(Collectors.toMap(
p -> p.getName(), //key
p -> p.getAge() //value
));
System.out.println(map); //{bb=1, cc=5}
//toArray
List<String> list = List.of("Apple", "Banana", "Orange");
String[] array = list.stream().toArray(String[]::new); //也可以返回arr数组
//注意到传入的“构造方法”是String[]::new,它的签名实际上是IntFunction<String[]>定义的String[] apply(int),即传入int参数,获得String[]数组的返回值。
//Collectors.groupingBy
List<String> list = List.of("Apple", "Banana", "Blackberry", "Coconut", "Avocado");
Map<String, List<String>> groups = list.stream()
.collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.toList()));
System.out.println(groups); //{A=[Apple, Avocado], B=[Banana, Blackberry], C=[Coconut]}
Optional
- Optional<T>类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
- Optional是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional方法
创建Optional类对象的方法:
Optional.of(T t)
:创建一个Optional实例,t必须非空;Optional.empty()
:创建一个空的Optional实例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接口实现提供的异常。
//Optional可以为null
Optional<person> person = Optional.of(new person("hh", 3));
person = null;
System.out.println(person); //打印结果为null
// T orElse(T other) 用法
person person = new person();
person = null;
Optional<person> p = Optional.ofNullable(person);
System.out.println(p.orElse(new person("aa", 1))); // person{name='aa', age=1}
Comments | NOTHING