zoukankan      html  css  js  c++  java
  • Java8新特性流(Stream) 仅此而已

    1、简介

         Java 8是Java自Java 5(发布于2004年)之后的最重要的版本。这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。在本文中我们一起来学习引入的一个新特性-流

    2、Lambda表达式

        在学习流之前,我们先来了解下java8中引入的Lambda表达式,它作为流很重要的一部分。Lambda表达式个构成如下所示:

        

    2.1  有效的Lambda表达式

    ()-> 42
    
    (Apple a) -> { return a.getWeight() > 150; }
    
     //对于函数只有一行代码的,可以去掉大括号{}以及return关键字
    (String s)-> s.length()
    
     (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

    2.2  方法引用

         当你需要使用方法引用时,目标放在分隔符::前,方法的名称放在后面。例如:Apple::getWeight就是引用了Apple类中定义的方法getWeight。请记住,不需要括号,因为你

    没有实际调用这个方法。方法引用就是Lambda表达式(Apple a) -> a.getWeight的快捷写法。

          Lambda及其等效方法引用的列子:

        

         方法引用主要有三类:

    •    指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)
    •    指向任意类型实例方法的方法引用(例如String的length方法,写作String::length)
    •    指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写成expensiveTransaction::getValue)

           第二种和第三种方法引用可能乍看起来有点儿晕。类似于String::length的第二种方法引用的思想就是你在引用一个对象的方法,而这个对象本身是Lambda的一个参数。例如,Lambda表达

           式(String s) -> s.toUppeCase()可以写作String::toUpperCase。但第三种方法引用指的是,你在Lambda中调用一个已经存在的外部对象中的方法。例如,Lambda表达式()->expensiveTransaction.getValue()可

           以写作expensiveTransaction::getValue。

    2.3  构造函数引用

    Supplier<Apple> c1 = Apple::new;     // 构造函数引用指向默认的Apple()构造函数
    Apple apple = c1.get(); // 调用Supplier的get方法将产生一个新的Apple
    
    Function<Integer, Apple> c2 = Apple::new;      // 指向Apple(Integer weight)的构造函数引用
    Apple apple2 = c2.apply(110); // 调用该Function函数的apply方法,并给出要求的重量,将产生一个Apple

         在下面的代码中,一个Integer构成的List中的每个元素都通过我们前面定义的类的map方法传递给了Apple的构造函数,得到了一个具有不同重量苹果的List:

    private static void test() {
    List<Integer> weights = Arrays.asList(7, 3, 4, 10);
    List<Apple> apples = map(weights, Apple::new);
        apples.stream().map(Apple::getWeight).forEach(System.out::println);
    }
    
    public static List<Apple> map(List<Integer> list, Function<Integer, Apple> f) {
        List<Apple> result = new ArrayList<>();
        for (Integer e : list) {
            result.add(f.apply(e));
        }
      return result;
    }

      3、引入流

         3.1流简介

              流是一系列数据项,一次只生成一项。程序可以从输入流中一个一个读取数据项,然后以同样的方式将数据项写入输出流。一个程序的输出流很可能是另一个程序的输入流。就想汽车组装流水线一

        样,汽车排队进入加工站,每个加工站会接收、修改汽车,然后将之传递给下一站做进一步的处理。尽管流水线实际上是一个序列,但不同加工站的运行一般是并行的。

              基于此,Java8可以透明的把输入的不想管部分拿到几个CPU内核上去分别执行你的Stream操作流水线——这是几乎免费的并行,用不着费劲搞Thread了。

          3.2流操作

                流操作包含3个部分:数据源、中间操作、终端操作

                   数据源:集合、数组

          中间操作:可以连接起来的流操作,流程一条流水线

          终端操作:关闭流的操作

        

        中间操作和终端操作,如下图所示:

        

       3.3 使用流

            筛选和切片:filter()、distinct()、limit()、skip()

                   映射:map()

         查找和匹配:anyMatch()、allMatch()、noneMatch()、findAny()、findFirst()

         归约:reduce()

           

       3.3.1 筛选和切片

                  filter():筛选过滤。会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流。

        distinct():去重。它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流

        limit(n):截断。它会返回一个不超过给定长度的流,如果流是有序的,则最多返回前n个元素

        skip(n):跳过。返回一个扔掉了前n个元素的流

    List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4,5, 6);
    numbers.stream()
    .filter(i -> i % 2 == 0)  //筛选出能被2整除的
    .distinct()  //去重
    .limit(3)  //取前3个元素
    .skip(1) //扔掉第一个元素
    .forEach(System.out::println);

      3.3.2 映射

        map():映射。它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素

        例如,下面的代码,提取菜单中菜的名字

    List<String> nameList = menu.stream()
      .map(Dish::getName)
      .collect(Collectors.toList())

      3.3.4 查找和匹配

        anyMatch():流中是否有一个元素能匹配给定的谓词

    List<Integer> numbers = Arrays.asList(6, 5, 4, 8, 1, 2, 3, 4, 2, 5, 6, 8, 10);
    numbers.stream().anyMatch(d -> d % 3 == 0) ;      //true

        allMatch():流中的元素是否都能匹配给定的谓词

    numbers.stream().allMatch(d -> d % 3 == 0);      //false

        noneMatch():与allMatch()相对,流中没有任何元素与给定的谓词匹配

    numbers.stream().noneMatch(d -> d % 7 == 0);       //true

        findAny():返回当前流中的任意元素。不过该方法返回的是Optional<T>容器类,其方法包括

              1、isPresent() 将在Optional包含值的时候返回true,否者返回false

              2、T get() 会在值存在时返回值,否则抛出一个NoSuchElement异常

    Optional<Integer> nums = numbers.stream().filter(d -> d % 4 == 0).findAny();
    nums.isPresent();    //true
    nums.get();      //4

        findFirst():找到第一个元素,工作方式类似于findAny()

      3.3.5 归约

        把一个流中的元素组合起来,使用reduce操作来表达更复杂的查询,比如“计算菜单中的总卡路里”或“菜单中卡路里最高的菜是哪一个”。此类查询需要将流中所有元素反复结合起来,得

      到一个值,比如一个Integer。这样的查询可以被归类为归约操作(将流归约成一个值)

        元素累加、累乘

    List<Integer> numbers = Arrays.asList(4, 5, 3, 9);
    
    //有初始值:
    numbers.stream().reduce(0, (a, b) -> a + b);      //21
    numbers.stream().reduce(0, Integer::sum);     //21
    numbers.stream().reduce(1, (a, b) -> a * b);    //540
    
    //无初始值:
    Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));
    //对于无初始值的归约,reduce操作无法返回其和,只能将结果包裹在一个Optional对象里,以表明可能不存在
    sum.get();    //21

        最大值、最小值

    List<Integer> numbers = Arrays.asList(4, 5, 3, 9);
    
    Optional<Integer> max = numbers.stream().reduce(Integer::max);    //最大值
    max.get();     //9
    
    Optional<Integer> min = numbers.stream().reduce(Integer::min);     //最小值
    min.get();   //3

      求和、平均数

    int total = menu.stream().collect(Collectors.summingInt(Dish::getCalories));     //统计总数
    double avg = menu.stream().collect(Collectors.averagingInt(Dish::getCalories)); // 求平均数
    long count = menu.stream().count();       //个数
    
    IntSummaryStatistics summ = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories)); summ.getAverage(); //平均数
    summ.getCount(); //个数
    summ.getMax(); //最大值
    summ.getMin(); //最小值
    summ.getSum(); //

     3.4 用流收集数据

      收集器(collect):是一个将数据流缩减为一个值的高级归约操作,这个值可以是集合、映射、或者一个值对象。你可以使用collect达到以下目的:

        1、将数据流缩减为一个单一值

        2、将一个数据流中的元素进行分组

        3、分割一个流中的元素

    public List<String> getList(List<Task> tasks) {        //将数据收集进一个列表
         return tasks.stream().map(Task::getTitle).collect(Collectors.toList()); 
    } 
    public Set<String> getSet(List<Task> tasks) {        //将数据收集进一个集合
         return tasks.stream().map(Task::getTitle).collect(Collectors.toSet()); 
    } 
    private static Map<String, Task> taskMap(List<Task> tasks) {        //将数据收集进一个映射
         return tasks.stream().collect(Collectors.toMap(Task::getTitle, task -> task));
    } 
    private static Map<TaskType, List<Task>> groupTasksByType(List<Task> tasks) {    //分组
         return tasks.stream().collect(Collectors.groupingBy(Task::getType)); 
    } 

    其它参照资源:http://blog.csdn.net/u013291394/article/details/52662761

  • 相关阅读:
    [转] 使用C#开发ActiveX控件
    [转] error LNK2026: 模块对于 SAFESEH 映像是不安全的
    Struts2详细说明
    a web-based music player(GO + html5)
    oracle_单向函数_数字化功能
    UVA 1364
    ORA-12545: Connect failed because target host or object does not exist
    左右v$datafile和v$tempfile中间file#
    二十9天 月出冲击黑鸟 —Spring的AOP_AspectJ @annotation
    Shell编程入门(再版)(在)
  • 原文地址:https://www.cnblogs.com/xuwenjin/p/8488698.html
Copyright © 2011-2022 走看看