java8的新特性学习

java8新增的新特性
1. 方法、lambda可作为值进行传递
2. 流(支持并行)
3. 接口能提供默认方法


lambda表达式

一、lambda表达式特点:

  1. 匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多
  2. 函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表
  3. 传递——Lambda表达式可以作为参数传递给方法或存储在变量中
  4. 简洁——无需像匿名类那样写很多模板代码

二、在哪里、如何使用lambda表达式

在哪里使用:

  1. 在函数式接口中使用lambda:
    函数式接口就是只定义一个抽象方法的接口,如:
public interface Comparator<T> {
    int compare(T o1, T o2);
}

public interface Runnable{
    void run();
}

public interface Callable<V>{
    V call();
}
  • 如何在函数式接口中使用lambda表达式?
Runnable r = () -> System.out.println("new thread");



public static void process(Runnable r){
        r.run();
}
process(r1);
  • java8在java.util.Function中提供了几种典型的函数式接口

    Precidate:用于接收泛型且返回boolean的情况

    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }

    Consumer:用于接收泛型且没有返回的情况

    @FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
    }

    Function:用于接收泛型T且返回泛型R的情况

    @FunctionalInterface
    public interface Function<T, R> {
        R apply(T t);
    }

    Supplier:用于不接收参数且返回泛型R的情况,同Callable

    @FunctionalInterface
    public interface Supplier<T> {
        T get();
    }

    使用实例见:http://blog.youkuaiyun.com/kevindai007/article/details/78982692

     tips:lamdba表达式中使用局部变量时,局部变量应显式申明为final,或只是用一次

    //正确
    String str = "sss";
    Supplier<String> supplier = () -> str;
    //编译报错
    String str = "sss";
    Supplier<String> supplier = () -> str;
    str = str.replace("a","b");

    2.在方法引用中使用lambda
    方法引用的可以理解为lamdba表达式的一种快捷写法

    //Function<String,Integer> valueOf = (s) -> Integer.valueOf(s);
    Function<String,Integer> valueOf = Integer::valueOf;
    //Function<String,Integer> length = (s) -> s.length();
    Function<String,Integer> length = String :: length;

    方法引用主要有四种

    1. 指向静态方法的方法引用

      (args) ->ClassName.staticMethod(args)
      
      ClassName::staticMethod
    2. 指向任意类型实例方法的方法引用

      (arg0,rest) -> arg0.instanceMethod(rest)
      
      ClassName.instanceMethod
    3. 指向现有对象的实例方法的方法引用

      (args) -> instance.instanceMethod(args)
      
      instance::instanceMethod
    4. 构造方法引用

      //Supplier<Apple> supply = () -> new Apply();
      Supplier<Apple> supply = Apple::new;
      Apple a1 = supply.get();
      
      //Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
      Function<Integer, Apple> c2 = Apple::new;
      Apple a2 = c2.apply(110);

一、什么是流

流就是”从支持数据处理操作的源生成的元素序列”

下面一步步进行剖析:
- 元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值
- 源——流会使用一个提供数据的源,如集合、数组或输入/输出资源
- 数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等

二、流与集合

集合与流之间的差异就在于什么时候进行计算。

集合是一个内存中的数据结构,它包含数据结构中目前所有的值(集合中的每个元素都是放在内存里的)——集合中的每个元素都得先算出来才能添加到集合中.

流则是在概念上固定的数据结构(不能添加或删除元素),其元素则是按需计算的。

用一句话来概括:集合关注的是数据与数据存储本身;流关注的则是对数据的计算

最重要的:流只能消费一次

List<String> title = Arrays.asList("HELLO", "WORLD");
        Stream<String> s = title.stream();
        s.forEach(System.out::println);
        s.forEach(System.out::println);

三、如何使用流

流的使用一般包括三件事:
- 个数据源(如集合)来执行一个查询;
- 个中间操作链,形成一条流的流水线;
- 个终端操作,执行流水线,并能生成结果

流的几种操作:

1、筛选

  1. filter

    Streams接口支持filter方法.该操作会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流

    List<Dish> vegetarianMenu = menu.stream().filter(Dish::isVegetarian)
    .collect(toList());
  2. distinct

    流支持distinct的方法,会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流

    List<Integer> list1 = Arrays.asList(800,23434,2933,800,900,800);
    list1.stream().distinct().forEach(System.out :: println);
  3. limit

    流支持limit(n)方法,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递 给limit

    List<Integer> list1 = Arrays.asList(800,23434,2933,800,900,800);
    list1.stream().limit(2).collect(toList());
    
  4. skip

    流支持skip(n)方法,返回一个去掉了前n个元素的流.如果流中元素不足n个,则返回一个空流

    List<Integer> list1 = Arrays.asList(800,23434,2933,800,900,800);
    list1.stream().skip(2).forEach(System.out :: println);

2、映射

  1. map

    流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映 射成一个新的元素

    List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et");
    List<Integer> collect = list2.stream().map(String::length).collect(toList());
  2. flatmap

    //查找数组中相同的字母
    List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et");
    list2.stream().map(s -> s.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList()).forEach(System.out::println);

3、查找和匹配

  1. anyMatch

    anyMatch方法用于检查流中是否有一个元素能匹配指定的条件.返回boolean,是终端操作

    List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et");
    if(list2.stream().anyMatch(s -> "kevin".equals(s))){
        System.out.println("kevin is in the team");
    }
  2. allMatch

    allMath方法用于检查流中是否所有元素都能匹配指定条件.返回boolean,是终端操作

    List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et");
    if(list2.stream().allMatch(s -> s.length() > 3)){
    System.out.println("all items are more than 3 letters");
    }else{
    System.out.println("not all items are more than 3 letters");
    }
  3. noneMath
    noneMath方法用于检查流中是否所有元素都不能匹配指定条件.返回boolean,是终端操作

    List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et");
    if(list2.stream().noneMatch(s -> "naruto".equals(s))){
        System.out.println("naruto is not in the team");
    }
  4. findAny

    findAny方法将返回当前流中的任意元素.

    ps.findAny方法返回Optional对象,后面咱们会详细讲Optional对象

    List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et");
    System.out.println(list2.stream().findAny().get());
  5. findFirst

    findFirst方法返回当前流中的第一个元素.

    List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et");
    System.out.println(list2.stream().findFirst().get());

    可以看到findFirst方法好像与findAny方法十分类似,那区别在哪里呢?答案是并行.findFirst在并行上限制较多.如果不关心返回的元素是哪个,推荐使用findAny,它在使用并行流时限制较少

4、归约

  1. reduce
    reduce方法用来表达复杂的查询,此类查询需要将流中元素反复结合起来得到一个值.

    List<Integer> list3 = Arrays.asList(1,2,3,4,5,6,7);
    //初始值为0,累加整个list
    System.out.println(list3.stream().reduce(0,(a,b) -> a + b));
    //System.out.println(list3.stream().count());
    //初始值为1,累乘整个list
    System.out.println(list3.stream().reduce(1,(a,b) -> a * b));

    reduce也可以不传入初始值,但会返回一个Optional对象

    List<Integer> list3 = Arrays.asList(1,2,3,4,5,6,7);
        System.out.println(list3.stream().reduce((a,b) -> a + b).get());

5、数值流

  1. Java 8引入了三个原始类型特化流:IntStream、DoubleStream和LongStream,分别将流中的元素特化为int、long和double

    • 把其他流转化为数值流

      List<String> list4 = Arrays.asList("1","2","3","4","5","6","7");
      int sum = list4.stream().mapToInt(s -> new Integer(String.valueOf(s))).sum();
      System.out.println(sum);
    • 数值流转化会对象流

      List<String> list4 = Arrays.asList("1","2","3","4","5","6","7");
          IntStream intStream = list4.stream().mapToInt(s -> new Integer(String.valueOf(s)));
          Stream<Integer> boxed = intStream.boxed();
    • 数值范围

      //取[0,100]之间的偶数求和
      int sum1 = IntStream.rangeClosed(1, 100).filter(i -> i % 2 == 0).sum();
      System.out.println(sum1);

四、构建流

1、由值创建

```
Stream<String> my = Stream.of("my", "name", "is", "kevindai");
my.map(String::toUpperCase).forEach(System.out::print);
//创建空流
Stream<Object> empty = Stream.empty();
```

2、由数值创建流

```
int[] nums = {1,2,3,4,5,6};
System.out.println(Arrays.stream(nums).sum());    
```

3、由文件生成流

```
Path path = Paths.get("/Users/daiwenkai/Downloads/ttt.html");
try(Stream<String> lines = Files.lines(path)) {
    lines.limit(10).forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}
```

4、由函数生成流

Stream API提供了两个静态方法来从函数生成流:Stream.iterate和Stream.generate.这两个操作可以创建无限流:不像从固定集合创建的流那样有固定大的流

一般来说,应该使用limit(n)来对这种流加以限制,避免产生无限多个值

  1. iterate

    //生成从0开始每次加2,取前10个数
    Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
    //生成斐波那契数列
    Stream.iterate(new int[]{0,1}, n -> new int[]{n[1],n[0] + n[1]}).limit(20).forEach(u -> System.out.println(u[0] + "," + u[1]));
  2. generate

    Stream.generate(Math::random).limit(5).forEach(System.out::println);
    
    //生成斐波那契数列
    IntSupplier intSupplier = new IntSupplier(){
            private int preValue = 0;
            private int nextValue = 1;
            @Override
            public int getAsInt() {
                int oldValue = preValue;
                preValue = nextValue;
                nextValue = oldValue + preValue;
                return oldValue;
            }
        };
        IntStream.generate(intSupplier).limit(10).forEach(System.out::println);

到这里JDK新特性的基本用法已经学习完毕,后面还会写一些介绍其他特性的文章.这篇文章前前后后也写了一个多月了(其实可能还不止),中间因为各种各样的原因中断了无数次,但好歹赶在年前写完了,哈哈哈,2018给自己这种拖拖拉拉的性格喊声加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值