第一章 为什么要关心java8

第一章 为什么要关心java8

  • java怎么又变了
  • 日新月异的计算应用背景:多核和处理大型数据集
  • 改进的压力:函数式比命令式更适应新的体系架构
  • java8的核心新特性:Lambda(匿名函数)默认方法
  1. 更简洁的代码,这些代码读起来更接近问题的描述
  2. 更简单的代码,更简单地使用了多核处理器
    • java8中提供了一个新的API(称为**“流”**,Stream),它能支持许多处理数据的并行操作,其思路和在数据库查询语言中的思路类似——用更高级的方式表达想要的东西,而由“实现”(在这里是Streams库)来选择最佳低级执行机制。
    • java8里面将代码传递给方法的功能还让我们能够使用一整套新技巧,通常称为函数式编程

1.1 java怎么还在变

1.1.1 java在编程语言生态系统中的位置

  • java是一个精心设计的面向对象的语言,有许多有用的库。有了集成的线程和锁的支持,它从第一天起就支持小规模并发。
  • 将java编译成JVM字节码(一种很快被每一种浏览器支持的虚拟机代码)意味着它成为了互联网applet(小应用)的首选。
  • 程序员越来越多地要处理所谓的大数据,并希望利用多核计算机或计算集群来有效地处理。这意味着需要使用并行处理——java以前对此并不支持。

1.1.2 流处理

  • 第一个编程概念是流处理

  • 是一系列数据项,一次只生成一项。程序可以从输入流中一个一个读取数据项,然后以同样的方式将数据项写入输入流。

  • 一个实际的例子是在Unix或Linux中,很多程序都从标准输入(Java中的System.in)读取数据,然后把结果写入标准输出(Java中的System.out)。

  • Unix命令:会(假设file1和file2中每行都只有一个词)先把字母转换成小写字母,然后打印出按照词典排序出现在最后的三个单词。


    Unix命令.png

  • 我们说sort把一个行流作为输入,产生了另一个行(进行排序)作为输出,如图1-2所示。请注意在Unix中,命令(cat,tr,sort和tail)是同时执行的,这样sort就可以在cat或tr完成前先处理头几行。

    操作流的Unix命令.png

  • 基于这一思想,Java8在java.util.stream中添加了一个Stream API;Stream<T>就是一系列T类型的项目。Stream API的很多方法可以链接起来形成一个复杂的流水线,就像先前例子里面链接起来的Unix命令一样。

  • 推动这种做法有两个好处:

    1.你可以站在更高的抽象层次上写Java8程序了:思路变成把这样的流变成那样的流(就像写数据库查询语句时的那种思路),而不是一次只处理一个项目。

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

1.1.3 用行为参数化把代码传递给方法

  • 另一个编程概念是通过API来传递代码的能力。

  • 在Unix的例子里,你可能想告诉sort命令使用自定义排序。虽然sort命令支持通过命令行参数来执行各种预定义类型的排序,比如倒序,但这毕竟有限。

  • 比方说,你有一堆发票代码,格式类似于2013UK00012014US0002…前面四位数代表年份,接下来的两个字母代表国家,最后四位是客户的代码。你可能想按照年份、国家、客户代码对发票进行排序。你真正想要的是,能够给sort命令一个参数让用户定义顺序:给sort命令传递一段独立代码。

  • java8增加了把方法(你的代码)作为参数传递给另一个方法的能力。

  • Stream API就是构建在通过传递代码使操作行为实现参数化的思想上的,当把compareUsingCustomerId传进去,你就把sort的行为参数化了。

    Ztn3Ux.png

1.1.4 并行与共享的可变数据

1.1.5 java需要演变

1.2 java中的函数

  • java程序可能操作的值

    • 原始值:42(int类型)、3.14(double类型)

    • 对象的引用

    • 函数(java8中新增,值的一种形式)

1.2.1 方法和Lambda作为一等公民

  • 我们介绍java8的第一个新功能是方法引用

    比方说,你想要的筛选一个目录的所有隐藏文件,老写法:

    /*
    		 * 接口是不可以实例化的。
    		 * new FileFilter(){}是匿名内部类的写法。
    		 * 它实例化了一个匿名内部类,而这个匿名内部类实现了FileFilter接口。
    		 */
    		/*
    		File[] hiddenFiles = new File(".").listFiles(new FileFilter(){
    			public boolean accept(File file){
    				return file.isHidden();
    			}
    		});
    		*/
    

    如今在Java8里,你可以把代码重写成这个样子:

    /*
    		 * java8里,你可以把代码重写成这个样子:
    		 */
    		File[] hiddenFiles = new File(".").listFiles(File::isHidden);
    

    你已经有了函数isHidden,因此只需用Java8的方法引用::语法(即“把这个方法作为值”)将其传给listFiles方法。

  • Lambda——匿名函数

    除了允许函数成为一等公民外,Java8还体现了更广义的将函数作为的思想,包括Lambda(匿名函数)。

    你现在可以写(int x)-> x+1,表示“调用时给定参数x,就返回x+1值的函数”。

1.2.2 传递代码:一个例子

完整代码:筛选苹果

  • 筛选出绿苹果

    public static List<Apple> filterGreenApples(List<Apple> inventory){
            List<Apple> result = new ArrayList<>();
            for(Apple apple:inventory){
                if("green".equals(apple.getColor())){
                    result.add(apple);
                }
            }
            return result;
        }
    
  • 筛选出超过150克的苹果

    public static List<Apple> filterHeavyApples(List<Apple> inventory){
            List<Apple> result = new ArrayList<>();
            for(Apple apple:inventory){
                if(apple.getWeight() > 150){
                    result.add(apple);
                }
            }
            return result;
        }
    

这两个方法只有一行不同:if语句那行。

  • Java8会把条件代码作为参数传递进去,这样可以避免filter方法出现重复的代码。

    public static boolean isGreenApple(Apple apple){
            return "green".equals(apple.getColor());
        }
    
        public  static boolean isHeavyApple(Apple apple){
            return apple.getWeight() > 150;
        }
    
        public interface Predicate<T>{
            boolean test(T t);
        }
    
        static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){
            List<Apple> result = new ArrayList<>();
            for(Apple apple:inventory){
                if(p.test(apple)){
                    result.add(apple);
                }
            }
            return result;
        }
    
  • 使用isGreenApple方法和isHeavyApple方法:

    List<Apple> inventory = Arrays.asList(new Apple(80,"green"),
                    new Apple(155,"green"),
                    new Apple(120,"red"));
    
    List<Apple> greenApples = filterApples(inventory, FilteringApples::isGreenApple);
    System.out.println(greenApples);
    
    List<Apple> heavyApples = filterApples(inventory, FilteringApples::isHeavyApple);
    System.out.println(heavyApples);
    
  • 输出结果:

    [Apple{color='green', weight=80}, Apple{color='green', weight=155}]
    [Apple{color='green', weight=155}]
    
  • 什么是谓词:

    前面的代码传递了方法FilteringApples::isGreenApplefilterApples方法,后者希望接受一个Predicate<Apple>参数。

    **谓词(predicate)**在数学上常常用来代表一个类似函数的东西,它接受一个参数值,并返回truefalse

    你在后面会看到,Java8允许你写Function<Apple,Boolean>,但用Predicate<Apple>是更标准的方式,效率也会更高一些。

1.2.3 从传递方法到Lambda

  • 把方法作为值来传递显然很有用,但要是为类似于isGreenAppleisHeavyApple这种可能只用一两次的短方法写一堆定义就优点烦人了。Java8中可以使用匿名函数(Lambda):

    filterApples(inventory, (Apple a)->"green".equals(a.getColor()));
    
    filterApples(inventory, (Apple a)->a.getWeight()>150);
    

    你甚至不需要为只用一次的方法写定义。

  • 但要是Lambda的长度多于几行的话,那你还是应该用方法引用来指向一个有描述性名称的方法,而不是使用匿名类的Lambda。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值