温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何使用java8新特性Stream

发布时间:2021-10-13 11:11:16 阅读:159 作者:iii 栏目:编程语言
Java开发者专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

这篇文章主要介绍“如何使用java8新特性Stream”,在日常操作中,相信很多人在如何使用java8新特性Stream问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何使用java8新特性Stream”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

首先先定义一个菜品类:

  • Dish

public class Dish {
    private final String name;
    private final boolean vegetarian;
    private final int calories;
    private final Type type;
    
    public boolean isVegetarian() {
        return vegetarian;
    }
    
    //省略set,get,toString方法}

然后创建一个静态方法,并且设置成类的成员变量,以供测试。

public static List<Dish> getDishes() {
    return Arrays.asList(
        new Dish("pork"false800, Dish.Type.MEAT),
        new Dish("beef"false700, Dish.Type.MEAT),
        new Dish("chicken"false400, Dish.Type.MEAT),
        new Dish("french fries"true530, Dish.Type.OTHER),
        new Dish("rice"true350, Dish.Type.OTHER),
        new Dish("pizza"true550, Dish.Type.OTHER),
        new Dish("prawns"false300, Dish.Type.FISH),
        new Dish("salmon"false450, Dish.Type.FISH)
    );
}

XNBqZ

好了,现在有个需求,找出菜品中所有小于400卡路里的食物,并且按照卡路里的大小进行排序。

java8之前,甚至有些人在java8之后,都会想着借助一个中间变量保符合要求的菜品,然后排序。

public static List<String> beforeJava8() {
    List<Dish> lowCaloricDishes = new ArrayList<>();
    for (Dish dish : dishes) {
        if (dish.getCalories() < 400) {
            lowCaloricDishes.add(dish);
        }
    }

    lowCaloricDishes.sort(Comparator.comparingInt(Dish::getCalories));//    lowCaloricDishes.sort((d1, d2) -> Integer.compare(d1.getCalories(), d2.getCalories()));    List<String> res = new ArrayList<>();

    for (Dish dish : lowCaloricDishes) {
        res.add(dish.getName());
    }
    return res;
}

由于前一篇文章讲过了方法引用,所以这里就直接用,不过下面一行也有普通的Lambda表达式的书写。

上述写法有什么问题吗,可以发现lowCaloricDishes 只使用了一次,真就一个临时变量。那能不能跳过创建变量的过程,你直接把数据给我,我经过过滤排序后得到想要的呢,就和流水线一样。

public static List<String> afterJava8() {
    return dishes.stream()
        .filter(dish -> dish.getCalories() < 400)
        .sorted(Comparator.comparing(Dish::getCalories))
        .map(Dish::getName)
        .collect(Collectors.toList());
}
流的定义

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

流和集合有点类似,集合是数据结构,主要的目的是存储和访问元素,而流的主要目的是为了对元素进行一系列的操作。

通俗入门地来讲,集合就相当于你一部电影下载,流就相当于在线观看。其实只需要把流想成高级的集合即可。流有两个重要的特点:

  • 流水线: 很多流本身会返回一个流,这样多个流就能链接起来和流水线一般。

  • 内部迭代: 内部迭代也就是把迭代封装起来,如collect(Collectors.toList) ,与之相对应的外部迭代则是for-each

值得注意的是,和迭代器类似,流只能遍历一次 ,遍历完就可以说这个流消费掉了。

流的构建

流的构建

流常用的构建方式有4种,其实要么是借助Stream 类的静态方法,要么是借助别人的类的静态方法。

  • 由值创建流

  • 由数组创建流

  • 由文件生成流

  • 由函数生成流

public static void buildStream() throws IOException {
    Stream<String> byValue = Stream.of("java8""c++""go");

    Stream<Object> empty = Stream.empty();

    int[] nums = {12345};
    IntStream byInts = Arrays.stream(nums);

    Stream<String> byFiles = Files.lines(Paths.get(""));

    Stream<Integer> byFunction1 = Stream.iterate(0, n -> n * 2);
    Stream<Double> byFunction2 = Stream.generate(Math::random);

    Stream<String> java = Stream.of("java");
}
流的操作

可以连接起来的流操作称为中间操作,关闭流的操作称为终端操作

通俗地讲,返回结果是流的操作称为中间操作,放回的不是流的操作称为终端操作。

如何使用java8新特性Stream

image-20210414155605342

通过查找java8接口可以得知到哪些接口是中间操作,哪些接口时终端操作。由于那些接口描述得太过官方,估计我贴了也没啥人会仔细看,所以想看的直接去官方查阅即可。

流的使用

就按照官网上的java API顺序来讲述,小插一句,之前我一直没有学流是主要是因为感觉接口会很多,怎么可能记得了这么多,其实这几天看才发现真的很少,基本上不用记。

img

img

首先构建好一个数字列表:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 5, 5, 5, 6, 7);

中间操作

中间操作有去重、过滤、截断、查看、跳过、排序 ,这些相信大家都能够明白是什么意思。

public static void midOperation() {
    numbers.stream()
        .distinct()
        .forEach(System.out::println);

    List<Integer> filter = numbers.stream()
        .filter(n -> n % 2 == 0)
        .collect(Collectors.toList());

    numbers.stream()
        .limit(3)
        .forEach(System.out::println);

    numbers.stream()
        .peek(integer -> System.out.println("consume operation:" + integer))
        .forEach(System.out::println);

    List<Integer> skip = numbers.stream()
        .skip(2)
        .collect(Collectors.toList());

    numbers.stream()
        .sorted()
        .forEach(System.out::println);
}
中间操作之映射(map)

需要单独拎出来说的是映射(map)扁平化映射(flatMap) ,注意,这里的map并不是hashmap的那个map,而是说把什么映射或者说转化成了什么。

public static void midOperation() {
 List<String> map = numbers.stream()
                .map(Object::toString)   //这里就是把int映射成了string                .collect(Collectors.toList());
}

而对于扁平化映射,现在又有一个需求,现在有个单词列表如{"hello", "world"},返回里面各不相同的字符,也就是要求返回List<String>

这还不简单,把单词映射成一个个字母,再去重就好了。

public static void flatMapDemoNoral() {
    List<String> words = Arrays.asList("hello""world");
    List<String[]> normal = words.stream()
        .map(str -> str.split(""))
        .distinct()
        .collect(Collectors.toList());
}

如何使用java8新特性Stream

img

虽然确实也能达到效果,但是注意映射所用的函数是split() ,返回的是String[] ,因此整个返回的是List<String[]>

那我映射完后再把每个String[] 数组映射成流

public static void flatMapDemoMap() {
    List<String> words = Arrays.asList("hello""world");
    List<Stream<String>> usingMap = words.stream()
                .map(str -> str.split(""))
                .map(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
}

虽然摘掉了数组的帽子,但是返回的却是List<Stream<String>>

flatMap 正是为了解决这种情况的

public static void flatMapDemoFlatMap() {
    List<String> words = Arrays.asList("hello""world");
    List<String> usingFlatMap = words.stream()
                .map(str -> str.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
}

可以简单的理解,map是把每个元素映射成了独立的流,而扁平化map是把元素保存了下来,最后映射成了一个流

查找与匹配

终端操作除了上述写例子的时候常用的collect()forEach() 还有查找和规约两种大的方向。

因为没啥好说的,直接上代码就完了:

public static void endOperationFindAndMatch() {
    if (dishes.stream().noneMatch(Dish::isVegetarian)) {
        System.out.println("所有的菜品都是非素食");
    }
    if (dishes.stream().allMatch(Dish::isVegetarian)) {
        System.out.println("所有的菜品都是素食");
    }
    if (dishes.stream().anyMatch(Dish::isVegetarian)) {
        System.out.println("菜品中至少有一道菜是素食");
    }

    Optional<Dish> any = dishes.stream()
        .filter(meal -> meal.getCalories() <= 1000)
        .findAny();
    Optional<Dish> first = dishes.stream()
        .filter(meal -> meal.getCalories() <= 1000)
        .findFirst();
}
归约(计算)

对流的规约操作的话,一般有普通操作也就是能直接调用接口的,还有一种就是借助reduce()

对于普通操作来说,像求和,最大值,最小值这些都是有接口对应的。

public static void endOperationCalculate() {
    long count = dishes.stream()
        .filter(meal -> meal.getCalories() <= 1000)
        .count();
    Optional<Dish> max = dishes.stream()
        .max(Comparator.comparingInt(Dish::getCalories));
    Optional<Dish> min = dishes.stream()
        .min(Comparator.comparing(Dish::getName));

}

但是如果说要求对元素求和,就要使用reduce()

image-20210417114209123

image-20210417114209123

一般使用的是可以接受2个参数,一个是初始值,一个是BinaryOprator<T> 来将两个元素结合起来产生的新值。

public static void reduceDemo() {
    List<Integer> numbers = Arrays.asList(1234555567);
    Integer sum = numbers.stream().reduce(0, Integer::sum);
    
    //所有元素相乘也是比较简单    Integer multi = numbers.stream().reduce(0, (a, b) -> a * b);
    
    //还有求最大值    Optional<Integer> max = numbers.stream().reduce(Integer::max);
}

Optional类

上面一直出现有返回值是Optional<T> ,它是一个容器类代表一个值存在或者不存在,比如一开始的findAny() ,可能找不到符合条件的菜品。Java8引入的目的主要是/为了不要返回容易出现问题的null了。

就说几个比较常用的api就好了至于其它的可以上网看下官方API,今天说的API已经够多了

  • isPresent() 将在Optional 包含值的时候返回true,否则返回false

  • ifPresent(Consumer<T> block) 存在值的时候会执行给定的代码块

  • get() 存在值就返回值,否则抛出NoSuchElement异常

  • orElse() 存在值就返回,否则就返回一个默认值

public static void optionalDemo() {
    //ifPresent    dishes.stream()
        .filter(Dish::isVegetarian)
        .findAny()
        .ifPresent(dish -> System.out.println(dish.getName()));

    //isPresent    boolean isLowCalories= dishes.stream()
        .filter(dish -> dish.getCalories() <= 1000)
        .findAny()
        .isPresent();
 
    //get    Optional<Dish> optional = dishes.stream()
        .filter(Dish::isVegetarian)
        .findAny();
    if (optional.isPresent()) {
        Dish dish = optional.get();
    }

    //orElse    Dish dishNormal = dishes.stream()
        .filter(Dish::isVegetarian)
        .findAny()
        .orElse(new Dish("java"false10000, Dish.Type.OTHER));
}

到此,关于“如何使用java8新特性Stream”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

原文链接:https://my.oschina.net/u/5078587/blog/5022845

AI

开发者交流群×