creed的博客

java8 新特性教程

· zhangwengang

代码地址:https://github.com/creedzwg/java8_action.git

讲解的新特性§

lambda 表达式§

什么是lambda表达式§

lambda 的语法§

例子:

 (String s) -> s.length()
 
 (Apple a) -> a.getWeight() > 150
 
(int x, int y) -> {
System.out.println("Result:");
System.out.println(x+y);
}

() -> 42

(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())


Lambda表达式其实是为FunctionalInterface(函数式接口)提供服务的 函数式接口的定义是接口内只有一个抽象方法,接口现在还可以有默认方法,(即在类没有对方法进行实现时, 其主体为方法提供默认实现的方法)

可以用4个接口来统一定义所有使用lambada表达式的行为


@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

}

Function 接口,有一个apply方法,这表示接受一个参数的类型,并返回另外一个参数的类型


@FunctionalInterface
public interface Consumer<T> {


    void accept(T t);
}

Consumer接口,有一个accept方法,他接受一个参数,没有任何返回值,可以做一些处理工作

@FunctionalInterface
public interface Predicate<T> {


    boolean test(T t);
}

Predicate接口,判断接口,有一个test方法,接受一个参数,返回boolean值

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

Supplier接口 ,提供者接口,有一个get方法,无参数,有返回值




这4个接口只是4个基本接口,还会有许多函数式接口对这些进行扩展,变种,就不详细一一介绍了

方法引用,Lambda表达式的另外一种快捷写法§

方法引用的工作方式§
(Apple a) -> a.getWeight()                   Apple::getWeight
() -> Thread.currentThread().dumpStack()     Thread.currentThread()::dumpStack
(str, i) -> str.substring(i)                 String::substring
(String s) ->  System.out.println(s)         System.out::println

####方法引用的分类

  1. 指向静态方法的方法引用(例如Integer.parseInt方法,可以写作Integer::parseInt)

Lambda: (args) -> ClassName.staticMethod(args) 方法引用: ClassName:: staticMethod

  1. 指向任意类型实例方法的方法引用(例如String 的length 方法, 写作String::length) 类似于String::length的第二种方法引用的思想就是你在引用一个对象的方法,而这个对象本身是Lambda的一个参数 Lambda: (arg1,rest) ->arg1.instanceMethod(rest) 方法引用: ClassName::instanceMethod
  2. 指向现有对象的实例方法的方法引用(假设你有一个局部变量a 用来存放 A类型的对象,它有个示例方法getValue,那么就可以写成a::getValue) 第三种方法引用指的是,你在Lambda中调用一个已经存在的外部对象中的方法 Supplier s =()->a.getWeight() Lambda: (args) ->instance.instanceMethod(args) 方法引用: instance::instanceMethod

具体例子§

List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareTo(s2));

可以修改成str.sort(String::compareTo)

构造函数引用§

Supplier<Apple> c1=Apple::new 等价于 Supplier<Apple> c1=()->new Apple()

lambda总结§

Stream 流§

流的概念§

Stream的用法§

  1. 一个数据源来执行一个查询,(创建stream)
  2. 一个中间操作链,形成一条流的流水线(中间操作)
  3. 一个终端操作,执行流水线,并能生成结果(终止操作)
//1. 可以通过集合顶级集合Collection的默认stream()或者parallelStream方法
List<String> list=new ArrayList<>();
Stream<String> stream1 = list.stream();

//2.通过Arrays中的静态方法stream() 获取数组流
Dish[] dishs=new  Dish[10];
Stream<Dish> stream2 = Arrays.stream(dishs);
//3. 通过Stream类中的静态方法of()
Stream<String> stream3 = Stream.of("aa", "bb", "cc");
//创建无限流
Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2);
iterate.limit(10).forEach(System.out::println);

//通过Stream的generate方式,也是无限流

Stream<Double> generate = Stream.generate(Math::random);
generate.limit(10).forEach(System.out::println);
  1. 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值"
操作 类型 返回类型 操作参数 函数描述符
filter 中间 Stream Predicate T -> Boolean
map 中间操作 Stream Function<T,R> T -> R
limit 中间操作 Stream
skip 中间操作 Stream
sorted 中间操作 Stream Comparator (T,T) ->Int
distinct 中间操作 Stream
foreach 终端 消费流中的每个元素并对其应用Lambda,操作返回void
count 终端 返回流中元素的个数,这一顿操作返回Long
collect 终端 把流规约成一个集合,比如List,Map甚至是Integer

stream API 的操作方式§

中间操作§

筛选和切片§

用predicate,筛选出不同的元素,忽略流中的的头几个元素,获奖流切断至指定长度

menu.stream().filter(Dish::isVegetarian)
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream().distinct()
menu.stream().filter(d -> d.getCalories() > 300).limit(3)
menu.stream().filter(d -> d.getCalories() > 300).skip(2)
映射§

一个非常常见的数据处理套路就是从某些对象中选择信息。比如在SQL里,你可以从表中选择一列。Stream API也通过map和flatMap方法提供了类似的工具

-flatMap他会接受一个参数作为函数,将流中每个值都换成另一个流,最后再将所有流拼成另外一个流

menu.stream().map(Dish::getName)

排序§

终止操作§

查找和匹配§
检查Predicate谓词是否至少匹配一个元素§
此操作用来查看菜单中是否有素食可以选择
if(menu.stream().anyMatch(Dish::isVegetarian)){
System.out.println("有素食");
}
anyMatch方法返回一个boolean,因此是一个终端操作 
检查谓词是否匹配所有元素§
是否菜单里面所有的菜的热量都小于1000卡路里
boolean  status= menu.stream().allMatch(d -> d.getCalories() < 1000);
检查谓词是否没有元素跟他匹配§
是否菜单里面所有的菜的热量都小于1000卡路里
menu.stream().noneMatch(d -> d.getCalories() >= 1000)

查找任意元素§

 Optional<Dish> dish =menu.stream().filter(Dish::isVegetarian).findAny();

可以看出,返回的是一个Optional<T> ,他是一个容器类,代表着一个值存在或者不存在,

在上面的代码中,findAny可能什么元素都没找到。Java 8的库设计人员引入了Optional<T>,这样就不用返回众所周知容易出问题的null了。


查找第一个元素§

Optional<Dish> dish =menu.stream().filter(Dish::isVegetarian).findFirst();

规约§

元素求和§

- reduce
对传统的数字列表的元素进行求和,我们可能会这么做
int sum = 0;
int[] numbers={4,5,3,9};
for (int x : numbers) {
sum += x;
}
numbers中的每个元素都用加法运算符反复迭代来得到结果。通过反复使用加法,你把一个
数字列表归约成了一个数字。这段代码中有两个参数:
1 总和变量的初始值,在这里是02 将列表中所有元素结合在一起的操作,在这里是+

这种就可以使用reduce操作来灵活完成
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
reduce 接受两个参数

1. 一个初始值,这个是0
2. 一个BinaryOperator<T>来将两个元素结合起来产生一个新值,这里我们用的是
lambda (a, b) -> a + b。

首先,0作为Lambda(a)的第一个参数,从流中获得4作为第二个参数(b)。0 + 4得到4,它成了新的累积值。然后再用累积值和流中下一个元素5调用Lambda,产生新的累积值9。接下来,再用累积值和下一个元素3调用Lambda,得到12。最后,用12和流中最后一个元素9调用Lambda,得到最终结果21

collect 搜集§