【java代码之美】---Java8 Stream

Stream

第一次看到Stream表达式就深深把我吸引,用它可以使你的代码更加整洁而且对集合的操作效率也会大大提高,如果你还没有用到java8的Stream特性,那就说明你确实out啦。

一、概述

1、什么是Stream

Stream是一种可供流式操作的数据视图有些类似数据库中视图的概念它不改变源数据集合如果对其进行改变的操作它会返回一个新的数据集合。

总的来讲它有三大特性:在之后我们会对照着详细说明

       1、stream不存储数据

       2、stream不改变源数据

       3、stream的延迟执行特性

2、Stream优点

  1. 代码简洁,函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环。
  2. 多核友好,Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下parallel()方法。

3、Stream API常用方法

Stream操作分类
中间操作(Intermediate operations) 无状态(Stateless) unordered() filter() map() mapToInt() mapToLong() mapToDouble() flatMap() flatMapToInt() flatMapToLong() flatMapToDouble() peek()
有状态(Stateful) distinct() sorted() sorted() limit() skip()
结束操作(Terminal operations) 非短路操作 forEach() forEachOrdered() toArray() reduce() collect() max() min() count()
短路操作(short-circuiting) anyMatch() allMatch() noneMatch() findFirst() findAny()

Stream上的所有操作分为两类:中间操作和结束操作,中间操作只是一种标记,只有结束操作才会触发实际计算。

中间操作又可以分为无状态的和有状态的:

无状态中间操作是指元素的处理不受前面元素的影响,而有状态的中间操作必须等到所有元素处理之后才知道最终结果,比如排序是有状态操作,在读取所有元素之前并不能确定排序结果;

结束操作又可以分为短路操作和非短路操作

短路操作是指不用处理全部元素就可以返回结果,比如找到第一个满足条件的元素。之所以要进行如此精细的划分,是因为底层对每一种情况的处理方式不同。

常用中间件

filter:过滤流,过滤流中的元素,返回一个符合条件的Stream

map:转换流,将一种类型的流转换为另外一种流。(mapToInt、mapToLong、mapToDouble 返回int、long、double基本类型对应的Stream)

flatMap:简单的说,就是一个或多个流合并成一个新流。(flatMapToInt、flatMapToLong、flatMapToDouble 返回对应的IntStream、LongStream、DoubleStream流。)

distinct:返回去重的Stream。

sorted:返回一个排序的Stream。

peek:主要用来查看流中元素的数据状态。

limit:返回前n个元素数据组成的Stream。属于短路操作

skip:返回第n个元素后面数据组成的Stream。

结束操作

forEach: 循环操作Stream中数据。

toArray: 返回流中元素对应的数组对象。

reduce: 聚合操作,用来做统计。

collect: 聚合操作,封装目标数据。

min、max、count: 聚合操作,最小值,最大值,总数量。

anyMatch: 短路操作,有一个符合条件返回true。

allMatch: 所有数据都符合条件返回true。

noneMatch: 所有数据都不符合条件返回true。

findFirst: 短路操作,获取第一个元素。

findAny: 短路操作,获取任一元素。

forEachOrdered: 暗元素顺序执行循环操作。

二、各种案例说明

多举点例子,以后忘记了还可以来找自己的博客,哈哈。

首先写一个领域对象

public class Person {

    private Integer  id;

    private String name;

    private String sex;

    private Integer age;

    //提供get,set,和满参构造函数
}

1、map中间件相关例子

public class TestMap {

    public static void main(String[] args) {
        List<Person> persionList = new ArrayList<Person>();
        persionList.add(new Person(1,"张三","男",8));
        persionList.add(new Person(2,"小小","女",2));
        persionList.add(new Person(3,"李四","男",25));
        persionList.add(new Person(4,"王五","女",20));
        persionList.add(new Person(5,"赵六","男",38));
        persionList.add(new Person(6,"大大","男",65));

    //1、只取出该集合中所有姓名组成一个新集合
        List<String> nameList=persionList.stream().map(Person::getName).collect(Collectors.toList());
        System.out.println(nameList.toString());

    //2、只取出该集合中所有id组成一个新集合
          List<Integer> idList=persionList.stream().mapToInt(Person::getId).boxed().collect(Collectors.toList());
         System.out.println(idList.toString());

    //3、list转map,key值为id,value为Person对象
         Map<Integer, Person> personmap = persionList.stream().collect(Collectors.toMap(Person::getId, person -> person));
         System.out.println(personmap.toString());

    //4、list转map,key值为id,value为name
         Map<Integer, String> namemap = persionList.stream().collect(Collectors.toMap(Person::getId, Person::getName));
         System.out.println(namemap.toString());
    }
}

运行结果:

是不是之前要好几层的for循环解决的问题,通过Stream只要一行代码就可以解决了。

这里要注意,如果你list转map的key如果不唯一,会报错,所以如果你不确定你的key是否唯一,可以改成如下:

 Map<Integer, String> map = persionList.stream().collect(
                Collectors.toMap(Person::getAge, Person::getName, (key1, key2) -> key1)
        );

2、filter相关例子

public class TestFilter {

    public static void main(String[] args) {
        List<Person> persionList = new ArrayList<Person>();
        persionList.add(new Person(1, "张三", "男", 8));
        persionList.add(new Person(2, "小小", "女", 2));
        persionList.add(new Person(3, "李四", "男", 25));
        persionList.add(new Person(4, "王五", "女", 8));
        persionList.add(new Person(5, "赵六", "女", 25));
        persionList.add(new Person(6, "大大", "男", 65));

        //1、查找年龄大于20岁的人数
        long  age=persionList.stream().filter(p->p.getAge()>20).count();
        System.out.println(age);

        //2、查找年龄大于20岁,性别为男的人数
       List<Person>  ageList=persionList.stream().filter(p->p.getAge()>20).filter(p->"男".equals(p.getSex())).collect(Collectors.toList());
        System.out.println(ageList.size());

    }
    /*
     *运行结果:
     *  3
     *  2
     */
}

3、sorted相关例子

对于数组举例

public class TestSort {

    String[] arr1 = {"abc","a","bc","abcd"};

    /**
     * 按照字符长度排序
     */
    @Test
    public void testSorted1_(){
        Arrays.stream(arr1).sorted(Comparator.comparing(String::length)).forEach(System.out::println);
        //输出:a、bc、abc、abcd
    }

    /**
     * 倒序
     * reversed(),java8泛型推导的问题,所以如果comparing里面是非方法引用的lambda表达式就没办法直接使用reversed()
     * Comparator.reverseOrder():也是用于翻转顺序,用于比较对象(Stream里面的类型必须是可比较的)
     * Comparator. naturalOrder():返回一个自然排序比较器,用于比较对象(Stream里面的类型必须是可比较的)
     */
    @Test
    public void testSorted2_(){
        Arrays.stream(arr1).sorted(Comparator.comparing(String::length).reversed()).forEach(System.out::println);
        //输出:abcd、abc、bc、a
        Arrays.stream(arr1).sorted(Comparator.reverseOrder()).forEach(System.out::println);
        //输出:bc、abcd、abc、a
        Arrays.stream(arr1).sorted(Comparator.naturalOrder()).forEach(System.out::println);
        //输出:a、abc、abcd、bc
    }

    /**
     * 先按照首字母排序
     * 之后按照String的长度排序
     */
    @Test
    public void testSorted3_(){
        Arrays.stream(arr1).sorted(Comparator.comparing(this::com1).thenComparing(String::length)).forEach(System.out::println);
    }    //输出:a、abc、abcd、bc
    public char com1(String x){
        return x.charAt(0);
    }
}

对于集合举例

public class TestSort {

    public static void main(String[] args) {
        List<Person> persionList = new ArrayList<Person>();
        persionList.add(new Person(1, "张三", "男", 8));
        persionList.add(new Person(2, "小小", "女", 2));
        persionList.add(new Person(3, "李四", "男", 25));
        persionList.add(new Person(4, "王五", "女", 8));
        persionList.add(new Person(5, "赵六", "女", 25));
        persionList.add(new Person(6, "大大", "男", 65));

        //1、找到年龄最小的岁数
        Collections.sort(persionList, (x, y) -> x.getAge().compareTo(y.getAge()));
        Integer age = persionList.get(0).getAge();
        System.out.println("年龄最小的有:" + age);
        //输出:年龄最小的有:2

        //2、找到年龄最小的姓名
        String name = persionList.stream()
                .sorted(Comparator.comparingInt(x -> x.getAge()))
                .findFirst()
                .get().getName();
        System.out.println("年龄最小的姓名:" + name);
        //输出:年龄最小的姓名:小小
    }
}

其它的就不具体写了。以后遇到特殊的再往里面补充。

参考

1、跟上 Java 8 – Stream API 快速入门

2、java8之stream

3、[Java进阶篇][函数式编程][Java 8+ Stream API]

4、深入理解Java Stream流水线

想太多,做太少,中间的落差就是烦恼。想没有烦恼,要么别想,要么多做。中校【10】

原文地址:https://www.cnblogs.com/qdhxhz/p/9399015.html

时间: 2024-08-04 18:50:23

【java代码之美】---Java8 Stream的相关文章

java代码之美(14)---Java8 函数式接口

Java8 函数式接口 之前写了有关JDK8的Lambda表达式:java代码之美(1)---Java8 Lambda 函数式接口可以理解就是为Lambda服务的,它们组合在一起可以让你的代码看去更加简洁. 一.概念 1.什么是函数式接口 概念 所谓的函数式接口, 当然首先是一个接口, 然后就是在这个接口里面 只能有一个抽象方法. 有关函数式接口,有个专门的注解叫:@FunctionalInterface.该注解主要特点有: 1.该注解只能标记在"有且仅有一个抽象方法"的接口上,表示函

java代码之美(16) ---Java8 Optional

摘自:https://www.cnblogs.com/qdhxhz/p/12056745.html Java8 Optional 一句话介绍Optional类:使用JDK8的Optional类来防止NullPointerException(空指针异常)问题. 一.前言 在我们开放过程中,碰到的异常中NullPointerException必然是排行第一的.所以在平时编码中,我们会时时的判断null. public void saveCity(City city) { if (city != nu

【java代码之美】---Java8 Map中的computeIfAbsent方法

Map中的computeIfAbsent方法 Map接口的实现类如HashMap,ConcurrentHashMap,HashTable等继承了此方法,通过此方法可以在特定需求下,让你的代码更加简洁. 一.案例说明 1.概述 在JAVA8的Map接口中,增加了一个方法computeIfAbsent,此方法签名如下: public V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 此方法首先判断缓存

【java代码之美】---guava之Immutable(不可变)集合

Immutable(不可变)集合 一.概述 guava是google的一个库,弥补了java语言的很多方面的不足,很多在java8中已有实现,暂时不展开.Collections是jdk提供的一个工具类. Guava中不可变对象和Collections工具类的unmodifiableSet/List/Map/etc的区别: 当Collections创建的不可变集合的wrapper类改变的时候,不可变集合也会改变,而Guava的Immutable集合保证确实是不可变的. 1.JDK中实现immuta

java8 Stream sorted()的一次调用链分析

代码 1 public static void main (String[] args) { 2 Stream.of("d2", "a2", "b1", "b3", "c") 3 .sorted((s1, s2) -> { 4 System.out.printf("sort: %s; %s\n", s1, s2); 5 return s1.compareTo(s2); 6 }) 7

java8 stream流操作

Stream 在对流进行处理时,不同的流操作以级联的方式形成处理流水线.一个流水线由一个源(source),0 到多个中间操作(intermediate operation)和一个终结操作(terminal operation)完成. 源:源是流中元素的来源.Java 提供了很多内置的源,包括数组.集合.生成函数和 I/O 通道等. 中间操作:中间操作在一个流上进行操作,返回结果是一个新的流.这些操作是延迟执行的. 终结操作:终结操作遍历流来产生一个结果或是副作用.在一个流上执行终结操作之后,该

写了10年JAVA代码,为何还是给人一种乱糟糟的感觉?

接触过不少号称写了10多年代码的程序员,可经常还是会发现他们的代码给人一种乱糟糟的感觉,那么如何才能写出让同事感觉不那么乱的代码呢? 一.为什么要写这篇文章 在开篇之前先说明下为什么要写这篇文章?在Java的世界里MVC软件架构模式绝对是经典的存在(PS:MVC是一种软件架构方式并不只有Java有),如果你是在最近十年前后进入Java的编程世界,那么你会发现自己这些年似乎从来没有逃离MVC架构模式的牢笼,只不过换着使用了不同的MVC框架,如早期的Struts1.Struts2以及现在几乎一统江湖

【转】Java8 Stream 流详解

  当我第一次阅读 Java8 中的 Stream API 时,说实话,我非常困惑,因为它的名字听起来与 Java I0 框架中的 InputStream 和 OutputStream 非常类似.但是实际上,它们完全是不同的东西. Java8 Stream 使用的是函数式编程模式,如同它的名字一样,它可以被用来对集合进行链状流式的操作. 本文就将带着你如何使用 Java 8 不同类型的 Stream 操作.同时您还将了解流的处理顺序,以及不同顺序的流操作是如何影响运行时性能的. 我们还将学习终端

何用Java8 Stream API进行数据抽取与收集

上一篇中我们通过一个实例看到了Java8 Stream API 相较于传统的的Java 集合操作的简洁与优势,本篇我们依然借助于一个实际的例子来看看Java8 Stream API 如何抽取及收集数据. 备注:上一篇内容:如何用Java8 Stream API找到心仪的女朋友 ## 目标&背景 我们以"处理订单数据"为例,假设我们的应用是一个分布式应用,有"订单应用","物流应用","商品应用"等都是独立的服务.本次我