java8(一)不断进化的java

当前软件行业的气候正在不断地变化。数据量的爆炸式增长,导致程序员在开发时不得不去面对存在的各种效率问题,并希望利用多核计算机或计算集群来有效地处理。这意味着需要使用并行处理,而曾经的java对于并行的支持并不好,这使得其不得不进行进化,以适应当前的行业气候。

Java 8中开发出并行和编写更简洁通用代码的功能,下面我们一起学习。

一、java8有哪些变化

1.1 流处理

Java 8在 java.util.stream 中添加了一个Stream API。通过这个流处理有两个较为直观的好处:

1)把这样的流变成那样的流。

什么意思?简单举个小例子,在Stream提供了很多方法,此处以mapToInt举例:

IntStream mapToInt(ToIntFunction<? super T> mapper);

用法:

    public static void main(String[] args) {
        // 有如下的字符串列表
        List<String> list = Arrays.asList("adas", "ada", "gfg", "asdasd", "adsasd");
        // 打印出每个字符串的长度
        list.stream().mapToInt(s->s.length()).forEach(l-> System.out.println(l));
        // 经过Lambda替换方法后,尽量的简化代码
        list.stream().mapToInt(String::length).forEach(System.out::println);
    }

结果:

4
3
3
6
6

在上面的代码我们看到只通过一行代码就可以完成操作,最大化的简化了代码。比传统的for循环更容易查看。

2)集合处理
几乎每个Java应用都会制造和处理集合。但集合用起来并不总是那么理想。比方说,你需要从一个列表中筛选金额较高的交易,然后按货币分组。你需要写一大堆套路化的代码来实现这个数据处理命令。

通过Stream API 可以简单的完成上面的步骤,这里举个例子:

    Map<Currency, List<Transaction>> transactionsByCurrencies =
            transactions.stream()
                    .filter((Transaction t) -> t.getPrice() > 1000)//筛选金额较高的
                    .collect(groupingBy(Transaction::getCurrency));//按照金额分组

相比于使用集合Collection,使用Stream API我们能获得两点直观的优点:
1)不需要自己去通过Collection API去迭代处理复杂的过程(外部迭代),使用流式处理的内部迭代,不需要操心循环的事情。
2)如果数据量非常庞大,那么使用集合的方式,处理将会非常慢;引入多线程将会是原本就复杂的集合迭代变得更加困难,使用Steam API可以较好的解决这一问题,看3)中的介绍。

3)在不同cpu上执行Stream操作,不需要Thread
Stream提供了并行操作,通过下面的例子简单看下:

    public static void main(String[] args) {
        // 有如下的字符串列表
        List<String> list = Arrays.asList("adas", "ada", "gfg", "asdasd", "adsasd");
        // 打印出每个字符串的长度
        //list.stream().mapToInt(s->s.length()).forEach(l-> System.out.println(l));
        // 经过Lambda替换方法后,尽量的简化代码
        long startTime = System.currentTimeMillis();
        list.stream().mapToInt(String::length).forEach(l -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(l);
        });
        System.out.println("耗时:" + (System.currentTimeMillis() - startTime));
    }

结果:

4
3
3
6
6
耗时:5104

这时我们添加上并行操作parallel,代码如下:

    public static void main(String[] args) {
        // 有如下的字符串列表
        List<String> list = Arrays.asList("adas", "ada", "gfg", "asdasd", "adsasd");
        // 打印出每个字符串的长度
        //list.stream().mapToInt(s->s.length()).forEach(l-> System.out.println(l));
        // 经过Lambda替换方法后,尽量的简化代码
        long startTime = System.currentTimeMillis();
        list.stream().mapToInt(String::length).parallel().forEach(l -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(l);
        });
        System.out.println("耗时:" + (System.currentTimeMillis() - startTime));
    }

结果:

3
6
4
3
6
耗时:1096

总体耗时基本是五分之一,或者可以使用parallelStream():

list.parallelStream().mapToInt(String::length).forEach....

关于具体的内容后面会详细讲解。

1.2 用行为参数化把代码(方法)传递给方法

Java 8增加了把方法(你的代码)作为参数传递给另一个方法的能力。我们把这一概念称为行为参数化

下面会简单讲解时什么意思。

1.2.1 函数

编程语言中的函数一词通常是指方法,尤其是静态方法。

Java 8中新增了函数:值的一种新形式

编程语言的本质就是操作值。按照传统的编程语言历史,我们以java举例:例如int,long,double这些可以直接被作为参数传递的值,都是一等值(一等公民);而还有很多涉及到结构的内容,例如方法和类等,不能作为参数传递,这些被称为二等公民。

java8中增加了新的功能,将二等公民添加到运行时传递,则二等公民就成为了一等公民。

1.2.1.1 方法和 Lambda 作为一等公民

Java 8的设计者决定允许方法作为值,让编程更轻松。此外,让方法作为值也构成了其他若干Java 8功能(如 Stream )的基础。

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

举个例子,你想要筛选一个目录中的所有隐藏文件。 File类里面有一个叫作 isHidden 的方法。我们可以把它看作一个函数,接受一个 File ,返回一个布尔值。但要用它做筛选,你需要把它包在一个 FileFilter 对象里,然后传递给 File.listFiles方法,如下所示:

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

在java8当中我们可以使用如下的方式:

    public void test(){
        File[] hiddenFiles = new File(".").listFiles(File::isHidden);
    }

你已经有了函数 isHidden ,因此只需用Java 8的方法引用 :: 语法(即“把这个方法作为值”)将其传给 listFiles 方法,我们也开始用函数代表方法了。

当使用File::isHidden时,其实创建了一个方法引用,与对象引用类似,我们就可以将其作为一等公民传递。

除了允许(命名)函数成为一等值外,Java 8还体现了更广义的将函数作为值的思想,包括Lambda(匿名函数)。直白说,将匿名函数作为一等值

将匿名函数传递有什么好处呢?写代码的时候相信都会遇到一种情况,当你只需要一个简单的运算的时候,还需要去重新定义一个类或者方法吗?如果没有方便的类或者方法可用的话,lambda就能帮助你使语法更简洁。

1.2.1.2 使用案例

假设有一个手机仓库,里面放了很多品牌的手机,包括苹果、华为等。现在我们需要筛选出华为手机有多少。那么需要些如下的代码:

    public List<Phone> brandFilter(){
        List<Phone> phones = new ArrayList<>();
        List<Phone> results = new ArrayList<>();
        for (Phone phone : phones) {
            if ("华为".equals(phone.getBrand())){
                results.add(phone);
            }
        }
        return results;
    }

如果又需要筛选黑色的手机,那么还需要写下面的方法:

    public List<Phone> colorFilter(){
        List<Phone> phones = new ArrayList<>();
        List<Phone> results = new ArrayList<>();
        for (Phone phone : phones) {
            if ("黑色".equals(phone.getColor())){
                results.add(phone);
            }
        }
        return results;
    }

这样一来重复代码就会很多了,如果我们使用java8,可以像下面这样写:

package com.cloud.bssp.java8.stream;

import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

/**
 * @description: 挑选手机
 * @author:weirx
 * @date:2021/10/15 16:02
 * @version:3.0
 */
public class ChoicePhone {

    public static void main(String[] args) {
        List<Phone> list = new ArrayList<>();
        list.add(new Phone("苹果", "黑色"));
        list.add(new Phone("华为", "黑色"));
        List<Phone> blackPhones = phoneFilter(list, ChoicePhone::isBlack);
        System.out.println(blackPhones.toString());
        List<Phone> huaweiPhones = phoneFilter(list, ChoicePhone::isHuawei);
        System.out.println(huaweiPhones.toString());
    }

    @Data
    static class Phone {
        private String brand;

        public Phone(String brand, String color) {
            this.brand = brand;
            this.color = color;
        }

        private String color;
    }

    /**
     * 筛选华为方法
     */
    public static boolean isHuawei(Phone phone) {
        return "华为".equals(phone.getBrand());
    }

    /**
     * 筛选黑色方法
     */
    public static boolean isBlack(Phone phone) {
        return "黑色".equals(phone.getColor());
    }

    /**
     * 手机过滤方法,参数是手机list和Predicate<T>函数
     */
    public static List<Phone> phoneFilter(List<Phone> phones, Predicate<Phone> p) {
        List<Phone> results = new ArrayList<>();
        for (Phone phone : phones) {
            if (p.test(phone)) {
                results.add(phone);
            }
        }
        return results;
    }
}

前面的代码传递了方法 Apple::isGreenApple (它接受参数 Apple 并返回一个boolean )给 filterApples ,后者则希望接受一个 Predicate<Apple> 参数。

Java 8也会允许你写 Function<Apple,Boolean>。

关于上面的代码请同学们细细体会啊,一遍看不懂就多看几遍。

前面就简单介绍了lambda的作用,像我们前面的例子中,只是一个判断的方法实在没有必要单独提供一个方法去维护,所以我们可以使用lambda的方式进行优化,就不需要单独定义判断方法isBlack和isHuawei了,使代码更简洁,如下所示:

List<Phone> blackPhones = phoneFilter(list, (Phone p) -> p.getColor().equals("黑色"));
List<Phone> huaweiPhones = phoneFilter(list, (Phone p) -> p.getBrand().equals("华为"));

List<Phone> blackPhones = phoneFilter(list, (Phone p) -> 
  p.getColor().equals("黑色") || p.getBrand().equals("华为")
);

1.3 默认方法

在java8之前的版本当中,接口被一个类实现,必须要求这个类实现该接口的全部方法,如果一个接口增加一个方法,那么所有的实现类都需要增加这个方法的实现。那么如何解决这个问题呢?

既然不想让实现类自己实现这个方法,那么只能由接口自己来实现了。这就给开发者提供了一个扩充接口的方式,而不会破坏现有的代码。在java8中使用关键字default来表示这个关键点。

比如在java8当中,我们可以直接调用List接口中的sort方法:

    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

这意味着 List 的任何实体类都不需要显式实现 sort。


本篇主要针对java的变化有个简单的认识,后续文章会逐渐深入到细节。

如果对您有帮助,给点个赞吧,感谢!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容