• 用不用lambda,这是一个问题


    什么是lambda表达式?

    众所周知,Java是一门强大的面向对象的语言,在Java中,除了8种基本的数据类型,其他一切皆为对象, 而数据和对数据的操作,是依赖于对象的属性和方法。面向对象的三大核心:封装、继承、多态都是对数据的抽象,而lambda提供了一种对行为抽象的编程模型。

    Java中将方法作为参数进行传递的方式被称为lambda表达式。

    lambda表达式是Java对于函数式编程的温和转变,面向对象编程和函数式编程不是互相对立的,结合使用能够更加有效地帮助我们管理程序的复杂性。

    为什么需要lambada?

    我认为有以下两点:将外部迭代转换为内部迭代使得效率更高、通过将函数作为参数使得编码更加优雅,更易读。

    随着摩尔定律的失效,个人电脑与专业服务器中开始配置多核处理器,为了使得程序的运行效率更高,开发者需要能够将任务分发到多个核心去执行,而Java Collection Framework默认的外部迭代方式将程序绑定在单个核心上运行,极大地降低了运算速度。

    有时候为了达到某一目的,我们需要重复很多样板代码,这些代码会侵入业务逻辑,降低了可读性。

    一个例子

    假设现在有这么一个例子:

    一个列表中拥有多个元素,元素类型为Integer,现在我要将其中每个元素通过转换为Point(java.awt.Point),最后得出所有Point中距离原点最远的那个。

    传统的写法是:

    public static void main(String[] args)
    {
    List<Integer> intList=Arrays.asList(21,82,33,54);
    List<Point>pointList=new ArrayList<>();
    for(Integer i:intList)
    {
    pointList.add(new Point(i%3,i));
    // 转换一
    }
    double max=Double.MIN_VALUE;
    for(Point p:pointList)
    {
    max =Math.max(p.distance(0,0),max);
    // 转换二
    }
    }

    这样写似乎没有什么不对,是的,它非常正确。但是依然存在着一些问题。

    首先,我们考虑一下这两个转换之间的关系。点Integer类型的A需要通过转换一变为Point类型,再由转换二将其变为Double类型,这没有问题。但是代码中的逻辑是当转换一全部完成后,再开始转换二。这似乎没有必要,并且程序将任务绑定在了一个核心上。假如转换任务是一系列复杂的计算,那么串行会导致效率低下。

    此外,我们的目的是intList -> Double(距离最大值),但是为了这一目的,在堆上为中间载体pointList分配了空间,假如这个List非常大,无疑会增加初始化以及GC消耗。而且,这段代码显得有点臃肿。

    匿名内部类 和 lambda

    我们首先使用匿名内部类对其进行改造,内部类是一种语法糖,修改之后虽然比之前简短了一些,但是仍然十分丑陋冗长,并且难以快速理解。


    List<Integer>intList =Arrays.asList(21,82,33,54);
    final double[] max = {Double.MIN_VALUE};
    intList.forEach(new Consumer<Integer>(){
    @Override
    public void accept(Integer i){
    max[0]=Math.max(new Point(i %3, i).distance(0,0), max[0]);
    }
    });
    那么,我们应该如何对其进行改造呢?首先我们来看看哪些东西其实是不需要的。因为List的泛型是Integer,所以编译器可以从上下文推断出来intList.forEach所要操作的元素类型是Integer,其次Consumer是一个interface,准确地来说他是一个函数式接口,我们重写了它的accept方法,它只是用来接收元素,所以我们可以摒弃这种啰嗦的写法,利用编译器自身根据上下文的类型推断,来书写更为精简的lambda表达式:
    List<Integer>intList=Arrays.asList(21,82,33,54);
    final double[]max={Double.MIN_VALUE};
    intList.forEach(i->{ max[0]=Math.max(new Point(i%3,i).distance(0,0), max[0]);

    Stream

    Stream是JDK1.8中新增的重要API,它可以帮助开发者操作对集合进行复杂的操作,并且能够自动依赖Fork/Join框架来实现自动并行化。

    在传统方式中,我们可以将数据想象成一种“小球”,而集合则是存放它的“瓶子”。那么上面的例子应该是这样:

    如果我们使用流呢?流提供了一个可选的有序值序列,而且无需为这些值提供任何存储。我们将其想象成一条河道,其中是流动的数据我们可以在河道上设立“滤网”,通过这些滤网在数据“流动”的时候就对其进行转化

    此时转化一/二都是对流进行操作,简洁了许多,代码如下:

    List<Integer>intList =Arrays.asList(21,82,33,54);
    OptionalDouble max=intList.stream()
    .map(i->new Point(i%3,i)) //操作一
    .mapToDouble(p->p.distance(0,0)) //操作二
    .max();

    将上述代码中的 stream换成 parallelStream,便可将intList中的元素转换为并行流再进行操作。

    总结:
    Java中的lambda表达式是对函数式编程的支持,在处理集合等问题上有诸多优势,能可见地提高代码质量,理想并且简洁地解决并发问题
    但并不代表函数式编程(FP)优于面向对象编程(OOP)。
    随发展,现在的Java程序大量使用反射、lambda等技术,已经不是单纯OOP语言。

    https://mp.weixin.qq.com/s/sbrUX-1a5FodEMmRHn4deQ

  • 相关阅读:
    (单例)使用同步基元变量来检测程序是否已运行
    使用委托解决方法的跨线程调用问题
    Rtmp/Hls直播、点播服务器部署与配置
    关于C#调用广州医保HG_Interface.dll调用的一些总结(外部组件异常)
    redhat7.3配置163 yum源
    模块化InnoSetup依赖项安装
    [迷宫中的算法实践]迷宫生成算法——递归分割算法
    [新手学Java]使用beanUtils控制javabean
    【HTML5】Canvas绘图详解-1
    【Swift 】- 闭包
  • 原文地址:https://www.cnblogs.com/Ly426/p/10307563.html
Copyright © 2020-2023  润新知