Java8新特性—stream流的使用

Java8新特性—stream流的使用

前言:

今天跟大家分享一下java8的新特性之一—流,肯定有很多人见到过,但是我相信目前很多人还没有广泛的使用流—可能暂时没有使用流的意识,或者说是使用的不熟练,如果真的是这样,那么今天分享的文章肯定会给你带来巨大的冲击,我们现在就来感受一下流的魅力。

在学习流的相关操作之前,希望先熟悉下lambda表达式和optional,这样比较容易理解。

一、热身运动

流可以帮助我们写出更优雅且高性能的代码,比如有这样一个场景,比如你有一个女朋友(没有的话就new一个吧),然后你女朋友的包包中装了很多的东西,现在需要将你女朋友的包包中的东西都拿出来,如果我们使用传统的迭代器,可能会是下面的场景:

你:媳妇儿,我们现在把你包包里的东西拿出来,包包里面有东西吗?

女朋友:有,有小镜子

你:好,把镜子拿出来放到桌子上,还有吗?

女朋友:有,充电宝

你:好,把充电宝拿出来放到桌子上吧,还有吗?

女朋友:有,还有纸巾

你:好,把纸巾拿出来放到桌子上,还有吗?

……

我们是不是感觉上面的对话看上去有点傻,其实这是我们在操作流或者数组的时候经常使用的逻辑,遍历整个容器,然后做判断或者操作。那么如果我们使用java8的流操作将会是下面的场景:

你:媳妇儿,把你包包里面的东西都拿出来放到桌子上

就是这么简单,我们下面来详细的学习一下流的操作

上面的实例可能有些人会觉得集合也能用一些清空的操作啊,比如clear或者addAll等操作啊,上面的例子完全不能凸显出流的特性,那接下来我们用一个代码的例子来初步的体验一下流会给我们什么?

需求:需要从一张菜谱中找出所有低热量的菜名,并且排序

package stream;

public class Dish {
    private final String name;

    private final boolean vegetarian;

    private final int calories;

    private final Type type;

    public Dish(String name, boolean vegetarian, int calories, Type type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public int getCalories() {
        return calories;
    }

    public Type getType() {
        return type;
    }

    public enum Type{
        MEAT,FISH,OTHER
    }
}

如果我们使用java8之前的操作可能是下面的实现代码

 

/***
 * 获取低热量的菜品的名称,并且按照热量从高到底排序
 * 热量<400的认为时低热量
 * java8之前的写法
 * @return
 */
public List<String> getSortLowCalories(List<Dish> dishes) {
    if (dishes == null || dishes.isEmpty()) return null;
    List<Dish> lowCaloriesSort = new ArrayList<>();

    for (Dish dish : dishes) {
        if (dish.getCalories() < 400) {
            lowCaloriesSort.add(dish);
        }
    }
    /*排序*/
    Collections.sort(lowCaloriesSort, new Comparator<Dish>() {
        @Override
        public int compare(Dish o1, Dish o2) {
            return Integer.compare(o1.getCalories(), o2.getCalories());
        }
    });
    List<String> lowDishNameSort = new ArrayList<>();
    /*获取名字*/
    for (Dish dish : lowCaloriesSort) {
        lowDishNameSort.add(dish.getName());
    }
    return lowDishNameSort;
}

先看下上面的代码,简单的分析一下:

  1. for循环遍历了两次,带来性能问题
  2. List<Dish> lowCaloriesSort仅仅是一个中间容器,即“垃圾变量”
  3. 代码写的很长,看上去不美观

那么我们引入java8代码将会编程怎么样子的呢?

/***
 * 使用java8流式的方式获取低热量的菜品的名称,并且排序
 * @param dishes
 * @return
 */
public List<String> ortLowCaloriesFor8(List<Dish> dishes) {
    List<String> lowDishNameSort = dishes.stream()
            .filter(dish -> dish.getCalories() < 400)
            .sorted(Comparator.comparing(Dish::getCalories))
            .map(Dish::getName)
            .collect(Collectors.toList());
    return lowDishNameSort;
}

看上去怎么样,是不是非常的精简,看上去是不是很棒,如果你也是这么认为,那么你可以继续往下看看,java8的新特性其实相对比较简单,一般都是一些操作性的内容,不像数据结构或并发编程、虚拟机等这些难以理解。

二、流的定义及描述

流:从支持数据处理操作的源生成的元素序列

看上去很青涩,很难理解,那就算了,我们尽量用一些比较容易理解的思维来转换对流的理解,比如我们可以将流的操作比作数据库的操作

如刚才上面获取低热量的菜名,并且排序可以写成下面的sql

Select name from dish where calorie < 400 order by calorie asc;

如果这个你能理解的话,那么我相信你对流应该有一个初步的了解,流的操作就像一个管道一样,流经过一个管道,那么流就变成了另外一个流,如上面的例子可以用下图来描述:

Java8新特性—stream流的使用

三、流的常用操作

上个类吧,代码虽然较多,但是非常简单

package stream;

import java.util.*;
import java.util.stream.Collectors;

public class StreamTest {

    /***
     * 获取低热量的菜品的名称,并且按照热量从高到底排序
     * 热量<400的认为时低热量
     * java8之前的写法
     * @return
     */
    public List<String> getSortLowCalories(List<Dish> dishes) {
        if (dishes == null || dishes.isEmpty()) return null;
        List<Dish> lowCaloriesSort = new ArrayList<>();

        for (Dish dish : dishes) {
            if (dish.getCalories() < 400) {
                lowCaloriesSort.add(dish);
            }
        }
        /*排序*/
        Collections.sort(lowCaloriesSort, new Comparator<Dish>() {
            @Override
            public int compare(Dish o1, Dish o2) {
                return Integer.compare(o1.getCalories(), o2.getCalories());
            }
        });
        List<String> lowDishNameSort = new ArrayList<>();
        /*获取名字*/
        for (Dish dish : lowCaloriesSort) {
            lowDishNameSort.add(dish.getName());
        }
        return lowDishNameSort;
    }

    /***
     * 使用java8流式的方式获取低热量的菜品的名称,并且排序
     * @param dishes
     * @return
     */
    public List<String> ortLowCaloriesFor8(List<Dish> dishes) {
        List<String> lowDishNameSort = dishes.stream()
                .filter(dish -> dish.getCalories() < 400)
                .sorted(Comparator.comparing(Dish::getCalories))
                .map(Dish::getName)
                .collect(Collectors.toList());
        return lowDishNameSort;
    }

    /************************************筛选和切片************************/

    //1、筛选
    /***
     * 获取是蔬菜的dish
     * @param dishes
     * @return
     */
    public List<Dish> getVegeterianDish(List<Dish> dishes) {
        if (dishes == null || dishes.isEmpty()) return null;

        List<Dish> vegeterianDishes = new ArrayList<>();

        for (Dish dish : dishes) {
            if (dish.isVegetarian()) {
                vegeterianDishes.add(dish);
            }
        }
        return vegeterianDishes;
    }

    /***
     * 使用java8获取蔬菜的dish
     * @param dishes
     * @return
     */
    public List<Dish> getVegeterianDishFor8(List<Dish> dishes) {

        return dishes.stream().filter(Dish::isVegetarian)
                .collect(Collectors.toList());
    }

    //2、distinct去重(和sql中的distinct一样)
    /***
     * 获取偶数,并且去重
     * @param numbers
     * @return
     */
    public Set<Integer> filterOddRemoveDuplicate(List<Integer> numbers){
        if (numbers == null || numbers.isEmpty()) return null;
        Set<Integer> results = new HashSet<>();
        for (Integer number : numbers){
            if (number %2 == 0){
                results.add(number);
            }
        }
        return results;
    }


    /***
     * 使用java8获取偶数,并且去重
     * @param numbers
     * @return
     */
    public List<Integer> filterOddRemoveDuplicateFor8(List<Integer> numbers){
        return numbers.stream().filter(number -> number %2 == 0)
                .distinct()
                .collect(Collectors.toList());
    }

    //3、limit使用(和sql中的limit一样)
    /***
     * 获取高热量的N道菜
     * @param dishes
     * @return
     */
    public List<Dish> getNHightCalories(List<Dish> dishes,int limit){
        if (dishes == null || dishes.isEmpty()) return null;

        List<Dish> results = new ArrayList<>();
        for (int i=0;i<limit;i++){
            results.add(dishes.get(i));
        }
        return results;
    }

    /***
     * 获取高热量的N道菜
     * @param dishes
     * @return
     */
    public List<Dish> getThreeHightCaloriesFor8(List<Dish> dishes,int limit){
        return dishes.stream().limit(limit).collect(Collectors.toList());
    }


    //4、skip使用(和支持skip的sql中的skip一样)
    /***
     * 跳过N个,去K个dish,类似与是数据库的分页操作
     * @param dishes
     * @param skipN 跳过n
     * @param k 取k个
     * @return
     */
    public List<Dish> skipNDish(List<Dish> dishes,int skipN,int k){
        if (dishes == null || dishes.isEmpty()) return null;
        /*假设数据合理,超过skipN+k个dish*/
        List<Dish> results = new ArrayList<>();
        for (int i = skipN;i<skipN+k;i++){
            results.add(dishes.get(i));
        }
        return results;
    }

    /***
     * 跳过N个,去K个dish,类似与是数据库的分页操作
     * @param dishes
     * @param skipN 跳过n
     * @param k 取k个
     * @return
     */
    public List<Dish> skipNDishFor8(List<Dish> dishes,int skipN,int k){
        return dishes.stream().skip(skipN)
                .limit(k).collect(Collectors.toList());
    }

    /*****************************映射*********************************/


    //5、map映射,类似数据中获取一列的数据
    /***
     * 获取菜品的名字
     * @param dishes
     * @return
     */
    public List<String> getDishNames(List<Dish> dishes){
        if (dishes == null || dishes.isEmpty()) return null;
        List<String> results = new ArrayList<>();
        for (Dish dish : dishes){
            results.add(dish.getName());
        }
        return results;
    }

    /***
     * 使用java8获取菜品的名字
     * @param dishes
     * @return
     */
    public List<String> getDishNamesFor8(List<Dish> dishes){
        return dishes.stream().map(Dish::getName).collect(Collectors.toList());
    }

    //6、flatmap映射,Flatmap可以理解成将一些内容打散操作
    /***
     * 获取字符中不同的单词,如["hello","world"]
     * 获取的结果['h','e','l','o','w','r','d']
     * @param words
     * @return
     */
    public Set<Character> getDifferentCharWord(List<String> words){
        if (words == null || words.isEmpty()) return null;
        Set<Character> charWords = new LinkedHashSet<>();
        for (String word : words){
            char[] chars = word.toCharArray();
            for (char c : chars){
                charWords.add(c);
            }
        }
        return charWords;
    }

    /***
     * 使用java8
     * 获取字符中不同的单词,如["hello","world"]
     * 获取的结果['h','e','l','o','w','r','d']
     * @param words
     * @return
     */
    public List<String> getDifferentCharWordFor8(List<String> words){
        return words.stream().map(word->word.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
    }

    /*******************查找和匹配******************************************/

    //7、匹配
    /***
     * allMatch、anyMatch、noneMatch
     * anyMatch:至少能匹配一个元素
     * allMatch:都能匹配
     * noneMatch:都不能匹配
     * 比如是匹配是否有蔬菜
     */
    /***
     * 查找是否有蔬菜
     * @param dishes
     * @return
     */
    public boolean existVegetarian(List<Dish> dishes){
        if (dishes == null || dishes.isEmpty()) return false;
        for (Dish dish : dishes){
            if (dish.isVegetarian()) return true;
        }
        return false;
    }

    /***
     * java8查找是否有蔬菜
     * @param dishes
     * @return
     */
    public boolean existVegetarianFor8(List<Dish> dishes){
        return dishes.stream().anyMatch(Dish::isVegetarian);
    }


    /***
     * 判断所有的菜是否健康,即所有菜的热量都<400
     * @param dishes
     * @return
     */
    public boolean isHeathy(List<Dish> dishes){
        if (dishes == null || dishes.isEmpty()) return false;
        for (Dish dish : dishes){
            if (dish.getCalories() > 400) return false;
        }
        return true;
    }

    /***
     * java8判断所有的菜是否健康,即所有菜的热量都<400
     * @param dishes
     * @return
     */
    public boolean isHeathyFor8(List<Dish> dishes){
        return dishes.stream().allMatch(dish -> dish.getCalories() < 400);
    }

    /***
     * 使用java8的noneMatch判断菜品是否健康
     * @param dishes
     * @return
     */
    public boolean isHeathFor8NoneMath(List<Dish> dishes){
        return dishes.stream().noneMatch(dish -> dish.getCalories() >= 400);
    }

    /***
     * 使用anymath判断菜品是否健康
     * 即存在任意的热量大于400的都是不健康的
     * @param dishes
     * @return
     */
    public boolean isHeathFor8AnyMatch(List<Dish> dishes){
        return !dishes.stream().anyMatch(dish -> dish.getCalories() >=400);
    }

    //8、查找
    /***
     * findAny:随机任意找到一个
     * findFirst:找到第一个
     * 他们返回的Optional,这个也是java8的新特性
     */
    /***
     * java8之前实现获取一道蔬菜
     * @param dishes
     * @return
     */
    public Dish findAnyVegetarian(List<Dish> dishes){
        if (dishes == null || dishes.isEmpty()) return null;
        for (Dish dish : dishes){
            if (dish.isVegetarian()){
                return dish;
            }
        }
        return null;
    }


    /***
     * java8实现获取一道蔬菜
     * @param dishes
     * @return
     */
    public Dish findAnyVegetarianFor8(List<Dish> dishes){
        return dishes.stream().filter(Dish::isVegetarian).findAny().orElse(null);
    }

    //
    /***
     * 找出第一个数的平方能被N整除的数字
     * @return
     */
    public Integer getFirstSquareDivisibleByN(List<Integer> numbers,int n){
        if (numbers == null || numbers.isEmpty()) return null;
        for (Integer number : numbers){
            if (Math.pow(number,2) % n == 0){
                return number;
            }
        }
        return null;
    }


    /***
     * 找出第一个数的平方能被N整除的数字
     * @return
     */
    public Integer getFirstSquareDivisibleByNFor8(List<Integer> numbers,int n){
        Optional<Integer> result = numbers.stream().map(number->number * number)
                .filter(number->number % n == 0)
                .map(number->(int)Math.sqrt(number))
                .findFirst();
        return result.orElse(null);
    }


    /*****************规约************************/


    //9、求和
    /***
     * java8之前实现求和
     * @param numbers
     * @return
     */
    public int getSum(int[] numbers){
        int result = 0;
        for (int number : numbers){
            result += number;
        }
        return result;
    }

    /***
     * java8求和
     * @param numbers
     * @return
     */
    public int getSumFor8(int[] numbers){
        return Arrays.stream(numbers).reduce(0,Integer::sum);
    }

    //10、获取最大值
    /***
     * 从数组中获取最大值
     * @param numbers
     * @return
     */
    public int getMax(int[] numbers){
        if (numbers == null || numbers.length == 0) return Integer.MIN_VALUE;
        int max = Integer.MIN_VALUE;
        for (int number : numbers){
            if (max < number) max = number;
        }
        return max;
    }

    /***
     * 使用java8从数组中获取最大值
     * @param numbers
     * @return
     */
    public int getMaxFor8(int[] numbers){
        return Arrays.stream(numbers).reduce(Integer::max).orElse(Integer.MIN_VALUE);
    }


    public static void main(String[] args) {
        StreamTest streamTest = new StreamTest();
        List<String> words = Arrays.asList("hello","world");
        //System.out.println(streamTest.getDifferentCharWord(words));
        //System.out.println(streamTest.getDifferentCharWordFor8(words));
        //List<Integer> numbers = Arrays.asList(1,2,3,4,5,6);
        //System.out.println(streamTest.getFirstSquareDivisibleByN(numbers,3));
        //System.out.println(streamTest.getFirstSquareDivisibleByNFor8(numbers,3));
        int[] numbers = {1,2,3,1000,55,333,112,333,555};
        System.out.println(streamTest.getSum(numbers));
        System.out.println(streamTest.getSumFor8(numbers));
    }


}

上面的代码很多没有经过测试,可能有些有点问题,或者有更好的实现方式,大家可以略过这一点,重点看java8的代码和java8之前进行对比,其实如果你看完了我相信对stream非常热爱,甚至可能会回去改代码了。

到这里流的基本的操作算是介绍完了,这篇文章就写道这里吧,后面会更加深入的还会介绍一些流的更高级的使用。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/111216.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 网站接入微信扫码登录并获取用户基本信息(微信开放平台)

    网站接入微信扫码登录并获取用户基本信息(微信开放平台)现在的网站基本都接入微信登陆了,就好像下面这样的。只需要用微信扫一下二维码,这个网站就可以展示你的微信昵称和头像,免去注册账号和输入密码登录的步骤,还免去设置头像和昵称的步骤,所以是挺方便的。那么如何把自己的网站接入这个呢?首先咱们得先在微信开放平台注册账号并且创建一个网站应用,等待审核通过后就可以获得Appid和AppSecretbingqie并且还要设置回调域名,这个不多说。首…

  • apache服务器搭建教程_apache本地服务器

    apache服务器搭建教程_apache本地服务器一、下载安装配置服务器1.下载1.百度搜索downlaodapache2.选择windows版本http://httpd.apache.org/download.cgi3.http://httpd.apache.org/docs/current/platform/windows.html#down4.下载下载解压后,目录结构2..配置配置文件位置:Apache24/conf/httpd.conf1.配置根目录(SRVROOT)$…

  • 分子排列不同会导致_《分子生物学》习题答案

    分子排列不同会导致_《分子生物学》习题答案《分子生物学》课后习题第1章绪论1.简述孟德尔、摩尔根和Waston等人对分子生物学发展的主要贡献。孟德尔是遗传学的奠基人,被誉为现代遗传学之父。他通过豌豆实验,发现了遗传学三大基本规律中的两个,分别为分离规律及自由组合规律。摩尔根发现了染色体的遗传机制,创立染色体遗传理论,是现代实验生物学奠基人。于1933年由于发现染色体在遗传中的作用,赢得了诺贝尔生理学或医学奖。Watson于1953年和克里…

  • datax(12):调度源码解读AbstractScheduler「建议收藏」

    datax(12):调度源码解读AbstractScheduler「建议收藏」datax的jobContainer最终会通过调度周期性的执行,今天把它看完;一、基类AbstractScheduler概述类继承关系全部方法二、AbstractScheduler的主要属性和方法1、主要属性/***脏数据行数检查器,用于运行中随时检查脏数据是否超过限制(脏数据行数,或脏数据百分比)*/privateErrorRecordCheckererrorLimit;/***积累容器通讯器,来处理JobContainer、Tas.

  • UART 和 USART 有区别

    UART 和 USART 有区别UART:universalasynchronousreceiverandtransmitter通用异步收发器          [BusSignal]  TX ,RX USART:universalsynchronousasynchronousreceiverandtransmitter通用同步异步收发器          [BusSignal]  T

  • 变脸不变质的桥梁模式(Bridge Pattern)

    变脸不变质的桥梁模式(Bridge Pattern)

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号