java8实战学习笔记_01_基础知识

1. Java 8的核心新特性:Lambda(匿名函数)、流、默认方法 。

  • Stream API
  • 向方法传递代码的技巧
  • 接口中的默认方法

       Java 8提供了一个新的API(称为“流”,Stream),它支持许多处理数据的并行操作,其思路和在数据库查询语言中的思路类似——用更高级的方式表达想要的东西,而由“实现”(在这里Streams库)来选择最佳低级执行机制。这样就可以避免用synchronized编写代码,这一代码不仅容易出错,而且在多核CPU上执行所需的成本也比你想象的要高。 

       从有点修正主义的角度来看,在Java 8中加入Streams可以看作把另外两项扩充加入Java 8的直接原因:把代码传递给方法的简洁方式(方法引用、Lambda)和接口中的默认方法

1.1 流处理
       第一个编程概念是流处理。介绍一下,是一系列数据项,一次只生成一项。程序可以从输入流中一个一个读取数据项,然后以同样的方式将数据项写入输出流。一个程序的输出流很可能是另一个程序的输入流。 类似于:


       基于这一思想, Java 8 java.util.stream 中添加了一个 Stream API Stream<T> 就是一系列 T 类型的项目。你现在可以把它看成一种比较花哨的迭代器。 Stream API 的很多方法可以链接起来形成一个复杂的流水线,就像先前例子里面链接起来的 Unix 命令一样。 
       推动这种做法的关键在于,现在你可以在一个更高的抽象层次上写 Java 8 程序了:思路变成了把这样的流变成那样的流(就像写数据库查询语句时的那种思路),而不是一次只处理一个项目。另一个好处是, Java 8 可以透明地把输入的不相关部分拿到几个 CPU 内核上去分别执行你的 Stream 操作流水线 —— 这是 几乎免费 的并行,用不着去费劲搞 Thread 了。后面会有详细介绍

1.2 用行为参数化把代码传递给方法
       Java 8中增加的另一个编程概念是通过API来传递代码的能力。 

1.3 并行与共享的可变数据
       第三个编程概念更隐晦一点,它来自我们前面讨论流处理能力时说的“几乎免费的并行”。 你需要放弃什么吗?你可能需要对传给流方法的行为的写法稍作改变。这些改变可能一开始会让你感觉有点儿不舒服,但一旦习惯了你就会爱上它们。你的行为必须能够同时对不同的输入 安全地执行 。一般情况下这就意味着,你写代码时不能访问共享的可变数据。这些函数有时被称为“纯函数”或“无副作用函数”或“无状态函数”。

2. Java 中的函数
       编程语言中的 函数 一词通常是指 方法 ,尤其是静态方法;这是在 数学函数 ,也就是没有副作用的函数之外的新含义。幸运的是,你将会看到,在 Java 8 谈到函数时,这两种用法几乎是一致的。
Java 8的设计者决定允许方法作为值,让编程更轻松。此外,让方法作为值也构成了其他若干Java 8功能(如Stream)的基
       我们介绍的Java8的第一个新功能是方法引用。比方说,你想要筛选一个目录中的所有隐藏文件。你需要编写一个方法,然后给它一个File,它就会告诉你文件是不是隐藏的。幸好,File类里面有一个叫作isHidden的方法。我们可以把它看作一个函数,接受一个File,返回一个布尔值。但要用它做筛选,你需要把它包在一个FileFilter对象里,然后传递给File.listFiles方法,如下所示:

File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
    public boolean accept(File file) {
        return file.isHidden();
    }
});

       呃!真可怕!虽然只有三行,但这三行可真够绕的。我们第一次碰到的时候肯定都说过:“非得这样不可吗?”我们已经有一个方法isHidden可以使用,为什么非得把它包在一个啰嗦的FileFilter类里面再实例化呢?因为在Java 8之前你必须这么做! 如今在Java 8里,你可以把代码重写成这个样子:
File[] hiddenFiles = new File(".").listFiles(File::isHidden);

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

法;请注意,我们也开始用函数代表方法了。稍后我们会解释这个机制是如何工作的。

传递代码:一个例子

package lambdasinaction.chap1;

import java.util.*;
import java.util.function.Predicate;

public class FilteringApples{

    public static void main(String ... args){

        List<Apple> inventory = Arrays.asList(new Apple(80,"green"),
                                              new Apple(155, "green"),
                                              new Apple(120, "red"));	

        // [Apple{color='green', weight=80}, Apple{color='green', weight=155}]
        List<Apple> greenApples = filterApples(inventory, FilteringApples::isGreenApple);
        System.out.println(greenApples);
        
        // [Apple{color='green', weight=155}]
        List<Apple> heavyApples = filterApples(inventory, FilteringApples::isHeavyApple);
        System.out.println(heavyApples);
        
        // [Apple{color='green', weight=80}, Apple{color='green', weight=155}]
        List<Apple> greenApples2 = filterApples(inventory, (Apple a) -> "green".equals(a.getColor()));
        System.out.println(greenApples2);
        
        // [Apple{color='green', weight=155}]
        List<Apple> heavyApples2 = filterApples(inventory, (Apple a) -> a.getWeight() > 150);
        System.out.println(heavyApples2);
        
        // []
        List<Apple> weirdApples = filterApples(inventory, (Apple a) -> a.getWeight() < 80 || 
                                                                       "brown".equals(a.getColor()));
        System.out.println(weirdApples);
    }

    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;
    }

    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;
    }

    public static boolean isGreenApple(Apple apple) {
        return "green".equals(apple.getColor()); 
    }

    public static boolean isHeavyApple(Apple apple) {
        return apple.getWeight() > 150;
    }

    public 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;
    }       

    public static class Apple {
        private int weight = 0;
        private String color = "";

        public Apple(int weight, String color){
            this.weight = weight;
            this.color = color;
        }

        public Integer getWeight() {
            return weight;
        }

        public void setWeight(Integer weight) {
            this.weight = weight;
        }

        public String getColor() {
            return color;
        }

        public void setColor(String color) {
            this.color = color;
        }

        public String toString() {
            return "Apple{" +
                   "color='" + color + '\'' +
                   ", weight=" + weight +
                   '}';
        }
    }

}
       在 Java 8 之前,筛选按颜色筛选苹果,你可能会写这样一个方法filterGreenApples。但是接下来,有人可能想要选出重的 苹果,比如超过 150 克,于是你心情沉重地写了filterHeavyApples。
       我们都知道软件工程中复制粘贴的危险——给一个做了更新和修正,却忘了另一个。嘿,这两个方法只有一行不同:if里面高亮的那行条件。如果这两个高亮的方法之间的差异仅仅是接受的重量范围不同,那么你只要把接受的重量上下限作为参数传递给filter就行了,比如指定(150,1000)来选出重的苹果(超过150克),或者指定(0,80)来选出轻的苹果(低于80克)。但是,我们前面提过了,Java8会把条件代码作为参数传递进去,这样可以避免filter方法出现重复的代码。现在你可以写在Apply类中写,isGreenApple和isHeavyApple方法。方法作为Predicate参数p传递进filterApples方法中。要用它的话,你可以写:filterApples(inventory,Apple::isGreenApple);或filterApples(inventory,Apple::isHeavyApple);
什么是谓词?
       前面的代码传递了方法Apple::isGreenApple(它接受参数Apple并返回一个boolean)给filterApples,后者则希望接受一个Predicate<Apple>参数。谓词(predicate)在数学上常常用来代表一个类似函数的东西,它接受一个参数值,并返回true或false。你在后面会看到,Java8也会允许你写Function<Apple,Boolean>——在学校学过函数却没学过谓词的读者对此可能更熟悉,但用Predicate<Apple>是更标准的方式,效率也会更高一点儿,这避免了把boolean封装在Boolean里面。为了更好地利用并行,Java的设计师没有这么做。Java8中有一整套新的类集合API——Stream,它有一套函数式程序员熟悉的、类似于filter的操作,比如map、reduce,还有我们接下来要讨论的在Collections和Streams之间做转换的方法。



以上文字均摘抄自《java8实战》,做为学习记录,如有侵权联系作者删除。













 





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值