宁波Java培训
达内宁波中心

0574-87236644

热门课程

达内讲解 Java8 流steam及Lambda表达式

  • 时间:2015-11-16
  • 发布:宁波达内
  • 来源:达内培训


    达内java培训专家指出,Java 8 主要变化是为集合框架增加了流的概念,提高了集合的抽象层次,流+高阶函数的外部处理方式对数据封装更好。同时流的概念使得对并发编程支持更强。

    语法方面, Java 8 提供了Lambda表达式来传递方法体,Lambda表达式体现了函数式编程的思想,使用了函数作参数/返回值的函数被称为高阶函数。

    一、 Lambda表达式

    以Runnable接口为例,如果需要执行一个线程,实际只需要run()方法中的代码块,但需要定义匿名内部类。

    使用Lambda表达式仅仅需要一行代码,达到传递run方法的效果,而不必定义匿名内部类。

new Thread(()->System.out.println("Lambda")).start();

    Lambda表达式由于Java的类型参数推断机制而非常简化。所有省略的内容都可以由编译器通过上下文推断出来。

    类型推断机制在Java中的应用广泛,推断Lambda表达式的目标类型,往往需要与Java的重载解析机制配合。解析规则:

    (1).只有一个可能目标类型时,由响应函数接口里的参数类型推导得出
    (2).有多个可能目标类型,选择最具体的类型
    (3).有多个可能目标类型但无法明确最具体类型,则编译报错

    正常的匿名内部类中 this关键字 指向内部类对象自身,同时将生成Apple$1.class文件。

    Lambda表达式中this所指向的则是外部类对象,并不会生成内部类class文件,既没有产生一个内部类,也没有引入一个新的作用域。

    Lambda与内部类相同之处在于其内部所定义的变量均为final或既成事实上的final.

    Java 8 最重要的改变就是对类库的改造,使得接口中方法可以拥有代码体。这种定义在接口中的包含方法体的方法,需要用default修饰,称之为默认方法。

    如果实现类中重写了默认方法,则接口中默认方法就被覆盖了。如果两个接口定义了相同的默认方法,则实现类中可以通过指定全称来确定使用哪个父类的方法。

    Lambda表达式可以作为函数参数和返回值,表示传递一个方法。方法引用就是使用 ClassName::MethodName 的形式来指定方法。方法引用完全可以与Lambda表达式互相替代。

    二、流stream

    流对象的高阶函数传入一个函数接口Predicate,避免了直接处理集合中的数据对象。流使用的通用格式:如下:

    (1).获得流对象Stream
    (2).对流对象Stream进行惰性求值,返回值仍然是一个Stream对象。
    (3).对流对象Stream进行及早求值,返回值不在是一个Stream对象。

    常见高阶函数 有以下7种:

    1.collect方法

    collect方法属于一个及早求值方法,负责将流对象转换成其他数据结构,如列表,集合,值等。这项工作由收集器Collector完成。java8为此提供了Collectors工具类。

    (1). 转换成集合

List<Person> list = stream.collect(Collectors.toList());
List<Person> arraylist = stream.collect(Collectors.toCollection(ArrayList::new)); 

Set<Person> set = stream.collect(Collectors.toSet());
Set<Person> treeSet = stream.collect(Collectors.toCollection(TreeSet::new));

    使用Collectors.toList()将流对象转换成集合时并不需要指定具体类型,Java默认选择了实现类型,如果要自己指定,可以使用Collectors.toCollection(ArrayList::new),其参数ArrayList::new就是上文中的方法引用,表示一个建立ArrayList对象的方法,ArrayList就是想要转换成的数据类型;

    (2).转换成值

//1.获得最大最小值
Function<Person, Integer> getLevel = p->p.age;
Comparator<Person> comparator = Comparator.comparing(getLevel);
stream.collect(Collectors.maxBy(comparator));
stream.collect(Collectors.minBy(comparator));
//2.获得平均值
ToIntFunction<Person> getAverage = p->p.age;
stream.collect(Collectors.averagingInt(getAverage));

    (3).数据分块

    将流对象按某种条件分成两部分

Predicate<Person> isTang = p->p.country.equals(Country.Tang);
stream.collect(Collectors.partitioningBy(isTang));

    (4).数据分组

Function<Person, Integer> country= p -> p.country.ordinal();
stream.collect(Collectors.groupingBy(country));

    分块和分组看似相同,但意义不同,分块使用判断作为方法,只能将流分成两块;分组则灵活的多。

    (5).字符串

stream.map(Person::getName).collect(Collectors.joining("/", "[", "]"));

    (6).合并收集器

stream.collect(Collectors.groupingBy(country,Collectors.counting()));

    2.map

    map是一个惰性求值方法。函数接口为Function<T, R>函数接口,负责将数据从一个类型转换为另一个类型;高阶函数map的作用就是将数据从一个流转换为另一个流。

    3.filter

    filter 是一个惰性求值方法。函数接口为Pridicate<T>,此方法负责对数据进行判断,filter高阶函数负责根据判断结果对流进行过滤。

    4.flatMap系列

    flatMap 是一个惰性求值方法。其参数亦为Function<T, R>,将多个流组合为一个流。

//1.a1,a2是两个列表,map处理后仍是两个列表
Stream.of(a1,a2).map(s->s)
-------------
[1, 2, 3, 4]
[]

//2.flatMap将二者合并为一个流
Stream.of(a1,a2).map(s->s)
.flatMap(s->s.stream())
-------------
1234

    看源码可知,flatMap中函数接口Function的输出类型为Stream<R>。
    5.max/min

    属于一个及早求值方法。需要传入一个Comparator函数接口,Java8提供了Comparator.comparing方法获得该函数接口的实现,该静态方法是接口的静态方法,获得一个函数返回一个Comparator对象。

min(Comparator.comparing(s->s.toString()));

    max/min的返回值是 Optional,代表一个或有或无的值,主要是用来取代万恶的null值;使用get方法可以获取其值。

    6.reduce

    属于一个及早求值方法。意为流数据的累加,有两个版本。

//1.无初始值累加
T t = person.stream().reduce((a,b)->a+b);
//2.带初始值累加
Optional<T> t = person.stream().reduce("1",(a,b)->a+b);

    7. foreach

    属于一个及早求值方法,用来遍历流对象。

上一篇:响应“编程一小时”达内推出少儿编程项目
下一篇:提高 Java中锁的性能的技巧

达内java大数据班就业喜报,最高月薪达18000元

795万高校毕业生创历史新高,2017届毕业生就业近况几何?

达内Linux学员毕业2周就业率96%,最高薪资10000元

达内教育总裁韩少云受邀出席GIE国际教育峰会做主题演讲

选择城市和中心
贵州省

广西省

海南省