博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java8用法总结
阅读量:4959 次
发布时间:2019-06-12

本文共 12020 字,大约阅读时间需要 40 分钟。

一、新特性

Java8带来了很多的新特性,本篇就以下几个方面,从实际用法的角度进行介绍。

  • Lambda 表达式
  • 函数式接口
  • Stream
  • 默认方法
  • Optional 类

 

二、Lambda表达式

2.1 引例

@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class Product {    private String id;    private Long num;    private Double price;}
为了以后排序,我们定义一种比较器,按价格排序:
Comparator
byPrice = new Comparator
() { @Override public int compare(Product o1, Product o2) { return o1.getPrice().compareTo(o2.getPrice()); }};
byPrice的作用是按价格比较2种产品,它是一种行为,可以用Lambda表达:
Comparator
byPrice = (Product o1, Product o2) -> o1.getPrice().compareTo(o2.getPrice();
这里只有一种类型Product,可根据Comparator
判断,因此进一步简化:
Comparator
byPrice = (o1, o2) -> o1.getPrice().compareTo(o2.getPrice();

 

2.2 概念

Lambda表示一种行为,通过Lambda表达式将行为参数化,这样,行为可以和对象一样传递;从第三章可以了解,Lambda表达式可以用函数式接口表示,Comparator就是一种函数式接口;

 

2.3 表达式

Lambda表达式有三部分,参数列表、"->"、Lambda主体,实际中有以下2种形式:

(parameters) -> expression(parameters) ->{ statements; }
(List
list) -> list.isEmpty; // 判断队列为空() -> new Product(); // 新建一个对象(String s) -> s.length; // 求字符串长度(Product p) -> System.out.println(p); // 输出对象

 

三、函数式接口

3.1 相关概念

函数式接口:只定义一个抽象方法的接口;它可能还会有很多默认方法,但有且仅有一个抽象方法;常见的函数式接口如Comparator, Runnable;函数式接口可以用来

表达Lamdba表达式;如将一个Lamdba表达式传递给一个函数式接口,即Lamdba表达式以内联的方式实现了函数式接口;

函数描述符:函数式接口的抽象方法;

 

3.2 引例

如果我们想写一个用于2个数计算的计算器,可能需要实现如下几个函数,根据运算符调用对应函数计算;

public 
T add(T a, T b);public
T add(T a, T b);public
T multiply(T a, T b);public
T divide(T a, T b);

换一种思路,如果有这样一个函数 public double func(double a, double b, Function f); f是一个函数式接口,它表示具体运算,具体代码实现如下:

@Log4j2public class T19 {    public static void main(String[] args) {        log.info(myFunction(1, 2, (a, b) -> a + b));        log.info(myFunction(1.0, 2.0, (a, b) -> a - b));        log.info(myFunction(BigDecimal.ZERO, BigDecimal.valueOf(2), (a, b) -> a.multiply(b)));    }    public static 
T myFunction(T a, T b, MyBiFunctionInterface
f) { return f.apply(a, b); }}@FunctionalInterfacepublic interface MyBiFunctionInterface
{ T apply(T a, T b);}

输出如下:

2018-09-01 19:39:11 - test.other.T19 INFO test.other.T19.main(T19.java:20) : 3

2018-09-01 19:39:11 - test.other.T19 INFO test.other.T19.main(T19.java:21) : -1.0
2018-09-01 19:39:11 - test.other.T19 INFO test.other.T19.main(T19.java:22) : 0

Java8提供了很多函数式接口,一般情况下不用去定义函数式接口,比如例子中MyBiFunctionInterface,可用BinaryOperator代替,BinaryOperator这个函数式接口,接收2个类型为T的参数,返回一个类型为T的结果,即(T, T) -> T,修改后如下:

public static 
T myFunction(T a, T b, BinaryOperator
f) { return f.apply(a, b); }

3.3 常见函数式接口

Function<T, R> T-> R
Predict<T> T -> boolean
Consumer<T> T -> void
Supplier<T> () -> T
UnaryOperator<T> T -> T
BinaryOperator<T> (T, T) -> T
BiFunction<T, U> (T, U) -> R
BiPredicate<L, R> (L, R) -> boolean
BiConsumer<T, U> (T, U) -> void

 

 

 

 

 

 

 

 

 

3.4 方法引用

Lamdba表达式的快捷写法,它更直观,可读性更好,比如:(Product p) -> p.getPrice  ==  Product::getPrice

方法引用主要有二类:

(1)指向静态方法;如 Integer::parseInt;

(2)指向实例方法:如 String::length;

(3)构造函数的方法引用:如Supplier<Product> p = Product::new;

例:第二章引例中还可以如下表达:

Comparator
c = Comparator.comparing(Product::getPrice);

 

3.5 复合

复合,就是将多个Lamdba表达式连接起来,表示更加复杂的功能;主要有以下三种

(1)函数复合:将Function代表的Lamdba复合起来,有andThen, compose;其中

f.andThen(g) = g(f(x)),先计算f表达式,将结果再计算g表达式;

f.compose(g) = f(g(x)),先计算g表达式,将结果再计算f表达式;

Function
f = x -> x + 1; Function
g = x -> x * 2; Function
h1 = f.andThen(g); // (1 + 1) * 2 = 4 Function
h2 = f.compose(g); // (1 * 2) + 1 = 3

(2)Predicate的复合,有negate, and, or,分别表示非、且、或,按从左到右的顺序

Predicate
p1 = a -> a.getPrice() > 100; // 大于100 Predicate
p2 = p1.negate(); // 小于等于100 Predicate
p3 = p1.negate().and(a -> a.getNum() > 10); // 价格小于等于100,且数量大于10

(3)比较器复合,如

Comparator
c = Comparator.comparing(Product::getPrice).reversed().thenComparing(Product::getNum);

 

四、流

4.1 概念

流用来处理数据集合,它具有如下特点:

(1)流强调的是计算,它是 源+数据处理,流将外部迭代(如for/while)转化为对我们透明的内部迭代;

(2)只能遍历一次,遍历完就关闭;

流具有如下优点:

(1)内置了很多常用方法(如排序、分类、统计);

(2)能透明的并行处理;

(3)声明式的,只需关注我要怎么样,不用关注我该如何实现,通过内置的方法与复合很容易实现;

 

4.2 流的操作

流的操作分为:

(1)中间操作:filter(Predicate<T>), map(Function(T, R), limit, sorted(Comparator<T>), distinct,flatMap;

(2)终端操作:只有终端操作才能产生输出,包括:allMatch, anyMatch, noneMatch, findAny, findFirst, forEach, collect, reduce, count

 

4.3 流的用法

@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class Product {    private String id;    private Long num;    private Double price;    private Boolean isUse;}
List
list = Lists.newArrayList( Product.builder().id("11").num(20l).price(100d).isUse(true).build(), Product.builder().id("12").num(25L).price(120d).isUse(true).build(), Product.builder().id("13").num(25L).price(100d).isUse(true).build(), Product.builder().id("14").num(20L).price(110d).isUse(false).build() );
 

(1)filter,  找出价格大于100的产品:

List
list1 = list.stream().filter(p -> p.getPrice() > 100).collect(Collectors.toList());

(2)distinct,去重

Arrays.asList(1, 2, 3, 1).stream().distinct().forEach(System.out::print); // 输出123

(3)limit,输出前n个

Arrays.asList(1, 2, 3, 1).stream().limit(2).forEach(System.out::print);  //输出12

(4)skip,跳过前n个

Arrays.asList(1, 2, 3, 1).stream().skip(2).forEach(System.out::print); // 输出31

(5)map, 映射,T -> R

Stream
map(Function
mapper);
list.stream().map(Product::getPrice).distinct().forEach(System.out::println);输出:100.0120.0110.0

(6)flatMap,扁平化,将每个元素产生的中间集合合并成一个大集合;接收的Function将T->Stream<R>

Stream
flatMap(Function
> mapper);
Arrays.asList(new String[]{"hello", "world"}).stream().map(p -> p.split(""))                .flatMap(Arrays::stream)  //.flatMap(p -> Arrays.stream(p))                .distinct().forEach(System.out::print);// 输出:helowrd

(7)匹配

boolean anyMatch(Predicate
predicate);
  • allMatch: 都满足条件才返回true;
  • anyMatch: 有一个满足就返回true;
  • noneMatch: 都不满足才返回true;
boolean b = Arrays.asList(1, 2, 3, 1).stream().anyMatch(p -> p > 2); //返回true

(8)查找,与其它操作结合使用

findAny:     Optional<T> findAny()

findFirst:     Optional<T> findFirst()

Arrays.asList(1, 2, 3, 4, 1).stream().filter(p -> p > 2).findAny() //输出Optional[3]Arrays.asList(1, 2, 3, 4, 1).stream().filter(p -> p > 2).findFirst() //输出Optional[3]

 

4.4 reduce归约

归约操作是很常用的操作,它将流中的值反复的结合起来,最终得到一个值,它是一种终端操作;

(1)Optional
reduce(BinaryOperator
accumulator);
(2)T reduce(T identity, BinaryOperator
accumulator);
(3) U reduce(U identity,              BiFunction
accumulator, BinaryOperator
combiner);

(1) 给定归约算法,最终归约成一个值,考虑到流可能为空,所以返回类型为Option,例:

Optional
op1 = Arrays.asList(1, 2, 3, 4, 1).stream().reduce(Integer::sum); //输出Optional[11]

(2)给定了初值,归约算法,返回结果;

Arrays.asList(1, 2, 3, 4, 1).stream().reduce(0, Integer::sum); //输出11 // Steam
中T为包装类型,没有sum,但Java8为流的原始类型提供了一些方法,如下Arrays.asList(1, 2, 3, 4, 1).stream().mapToInt(a -> a).sum();list.stream().mapToLong(Product::getNum).sum();

(3)第三个参数表示合并方式,当是并行流时,各线程独立计算结果,最后将各线程的结果合并;

BiFunction
f1 = (Double a, Product b) -> a + b.getNum(); BinaryOperator
f2 = (a, b) -> a + b; double b2 = list.parallelStream().reduce(0d, f1, f2); log.info(b2); //输出90

 

4.5 数值流

数值流除了具有流的方法外,还有一些特殊的统计方法,例

DoubleStream doubleStream = list.stream().mapToDouble(Product::getPrice);double average = doubleStream.average().getAsDouble();//数值流->对象流Stream
sd = doubleStream.boxed();
// 生成n以内的勾股数Stream
stream = IntStream.rangeClosed(1, 30).boxed().flatMap(a -> IntStream.rangeClosed(a, 30).mapToObj(b -> new double[]{a, b, Math.sqrt(a * a + b * b)}).filter(t -> t[2] % 1 == 0));stream.limit(3).forEach(t -> System.out.println(t[0] + ", " + t[1] + ", " + t[2]));输出:3.0, 4.0, 5.05.0, 12.0, 13.06.0, 8.0, 10.0

 

4.6 构建流

Stream.iterate(0, n -> n + 2).limit(10);Stream.generate(Math::random).limit(10);

 

五、收集器(collect归约)

5.1 常见用法

Map
> map = list.stream().collect(groupingBy(Product::getPrice));Long allNum = list.stream().collect(summingLong(Product::getNum));double average = list.stream().collect(averagingDouble(Product::getPrice));LongSummaryStatistics statistics = list.stream().collect(summarizingLong(Product::getNum));String ids = list.stream().map(Product::getId).collect(joining(", "));

 

5.2 reducing归约

Optional
opp = list.stream().collect(reducing((a, b) -> a.getPrice() > b.getPrice() ? a : b));long allNum2 = list.stream().collect(reducing(0L, Product::getNum, Long::sum));long allNum3 = list.stream().collect(reducing(0L, Product::getNum, (i, j) -> i + j));

collect中reducing归约三要素,初值,提取值,归约方法,若无初值返回Optional,若提取值即是对象本身,可省略;

 

5.3 多重分组

Map
>> map = list.stream().collect(groupingBy(Product::getPrice, groupingBy(Product::getNum)));Map
>> map2 = list.stream().collect(groupingBy(Product::getPrice, groupingBy(p -> { if (p.getNum() <= 80L) return "little"; else if (p.getNum() >= 120L) return "many"; else return "normal";})));System.out.println(JacksonUtil.toJson(map));System.out.println(JacksonUtil.toJson(map2));

输出如下:

{  "100.0" : {    "20" : [ {      "id" : "11",      "num" : 20,      "price" : 100.0,      "isUse" : true    } ],    "25" : [ {      "id" : "13",      "num" : 25,      "price" : 100.0,      "isUse" : true    } ]  },  "110.0" : {    "20" : [ {      "id" : "14",      "num" : 20,      "price" : 110.0,      "isUse" : false    } ]  },  "120.0" : {    "25" : [ {      "id" : "12",      "num" : 25,      "price" : 120.0,      "isUse" : true    } ]  }}{  "100.0" : {    "little" : [ {      "id" : "11",      "num" : 20,      "price" : 100.0,      "isUse" : true    }, {      "id" : "13",      "num" : 25,      "price" : 100.0,      "isUse" : true    } ]  },  "110.0" : {    "little" : [ {      "id" : "14",      "num" : 20,      "price" : 110.0,      "isUse" : false    } ]  },  "120.0" : {    "little" : [ {      "id" : "12",      "num" : 25,      "price" : 120.0,      "isUse" : true    } ]  }}

在一次分组的子集合中处理数据

Map
map = list.stream().collect(groupingBy(Product::getPrice, counting()));Map
> map2 = list.stream().collect(groupingBy(Product::getPrice, maxBy(comparingLong(Product::getNum))));Comparator
c = ((p1, p2) -> p1.getNum().compareTo(p2.getNum()));Map
> map3 = list.stream().collect(groupingBy(Product::getPrice, maxBy(c)));Map
map4 = list.stream().collect(groupingBy(Product::getPrice, collectingAndThen(maxBy(comparing(Product::getNum)), Optional::get)));

 

5.4 分区

由一个谓词作为分类,分为2类,true与false,用法与groupingBy完全一样

Map
> map = list.stream().collect(partitioningBy(Product::getIsUse));Map
>> map2 = list.stream().collect(partitioningBy(Product::getIsUse, groupingBy(Product::getPrice)));Map
map3 = list.stream().collect(partitioningBy(Product::getIsUse, summarizingLong(Product::getNum)));Map
map4 = list.stream().collect(partitioningBy(Product::getIsUse, averagingLong(Product::getNum)));

 

六、optional

6.1 使用

(1)单级包装用法:我们会见到如下代码, 

String name = null;if (product != null) {    name = product.getId();}

利用optional可转化为

Optional
optProduct = Optional.ofNullable(product);Optional
optName = optProduct.map(Product::getId);

(2)多级包装用法

public String getName(Person person) {    return person.getCar().getInsurance().getName();}

经过包装如下,注意为防止Optional<Optional<T>>这种中间结果造成编译不通过,需要使用flatMap

public String getName(Person person) {    Optional
optPerson = Optional.ofNullable(person); return optPerson.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknown");}

 

 

 

 

转载于:https://www.cnblogs.com/whuxz/p/9570613.html

你可能感兴趣的文章
[Xcode 实际操作]五、使用表格-(1)使用UITableView制作简单表格
查看>>
Mysql net start mysql启动,提示发生系统错误 5 拒绝访问,原因所在以及解决办法
查看>>
设计模式--门面模式C++实现
查看>>
Android SurfaceFlinger服务(五) ----- VSync信号的产生
查看>>
[Algorithm -- Dynamic programming] How Many Ways to Decode This Message?
查看>>
[SVG] Combine Multiple SVGs into an SVG Sprite
查看>>
[AngularJS + Webpack] Requiring CSS & Preprocessors
查看>>
[Angular 2] ng-control & ng-control-group
查看>>
WEB前端页面常用CSS简写优化技巧
查看>>
约瑟夫环问题
查看>>
AI Conditional GAN
查看>>
结对编程_四则表达式生成
查看>>
SD卡状态变动receiver接收不到的问题
查看>>
Windows 7 SP1无人值守自动应答文件制作
查看>>
如何用WordPress做网站?
查看>>
Linux下gcc,g++,gdb,scon部分用法笔记
查看>>
Spring boot 1: 使用IDEA创建Spring boot项目
查看>>
经典SQL语句大全(转)
查看>>
多表查询
查看>>
assign和weak的深层次解析
查看>>