Java基础知识09--Stream API详解01(java 8的map中使用stream)

1.Stream 介绍

Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利

Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。

Stream可以由数组或集合创建,对流的操作分为两种:

  (1) 中间操作,每次返回一个新的流,可以有多个。
  (2) 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到处理的结果。

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

以上的流程转换为 Java 代码为:

List<Integer> transactionsIds = 
widgets.stream()
             .filter(b -> b.getColor() == RED)
             .sorted((x,y) -> x.getWeight() - y.getWeight())
             .mapToInt(Widget::getWeight)
             .sum();

Stream流的操作流程一般都是这样的,先将集合转为流,然后经过各种操作,比如过滤、筛选、分组、计算。最后的终端操作,就是转化成我们想要的数据,这个数据的形式一般还是集合,有时也会按照需求输出count计数。

另外,Stream有几个特性:

  • stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
  • stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
  • stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

 

 

 2.Stream的创建

Stream可以通过集合数组创建。

1、通过 java.util.Collection.stream() 方法用集合创建流

List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();

2、使用java.util.Arrays.stream(T[] array)方法用数组创建流

int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);

3、使用Stream的静态方法:of()、iterate()、generate()

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println);

Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);

streamparallelStream的简单区分: stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选集合中的奇数,两者的处理不同之处:

 

 

如果流中的数据量足够大,并行流可以加快处速度。

除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流:

Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst();

3.Stream的使用

在使用stream之前,先理解一个概念:Optional 。

Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

3.1 遍历/匹配(foreach/find/match)

Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。Stream的遍历、匹配非常简单。

 3.1.1 forEach方法

 案例一:forEach 循环

    public static void main(String[] args) {
        //对集合进行遍历
        List<String> asList = Arrays.asList("you","don't","bird","me","!");
        // 方式一:JDK1.8之前的循环方式
        for (String str : asList) {
            System.out.println(str);
        }
        System.out.println("---------------------2--------------------------");
        // 方式二:使用Stream的forEach方法
        asList.stream().forEach(x-> System.out.println(x));
        System.out.println("---------------------3--------------------------");
        // 方式二:使用Stream的forEach方法简写方式
        asList.stream().forEach(System.out::println);
        //注:其实在数据量小时推荐使用增强for循环,如果在大数据量(大数据分析)下可以使用新的循环

    }

效果图:

3.1.2 filter方法

案例二:filter使用

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);

        // 遍历输出符合条件的元素
        list.stream().filter(x -> x > 6).forEach(System.out::println);
        // 匹配第一个
        Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
        // 匹配任意(适用于并行流)
        Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
        // 是否包含符合特定条件的元素
        boolean anyMatch = list.stream().anyMatch(x -> x < 6);
        System.out.println("匹配第一个值:" + findFirst.get());
        System.out.println("匹配任意一个值:" + findAny.get());
        System.out.println("是否存在大于6的值:" + anyMatch);

    }

执行结果:

7
9
8
匹配第一个值:7
匹配任意一个值:8
是否存在大于6的值:true

3.1.3 anyMatch,allMatch方法

案例3

    long count();  
  
    boolean anyMatch(Predicate<? super T> predicate);  
  
    boolean allMatch(Predicate<? super T> predicate);  
  
    boolean noneMatch(Predicate<? super T> predicate);

anyMatch表示,判断的条件里,任意一个元素成功,返回true

allMatch表示,判断条件里的元素,所有的都是,返回true

noneMatch跟allMatch相反,判断条件里的元素,所有的都不是,返回true

    public static void main(String[] args) {
        List<String> strs = Arrays.asList("a", "a", "a", "a", "b");
        boolean aa = strs.stream().anyMatch(str -> str.equals("a"));
        boolean bb = strs.stream().allMatch(str -> str.equals("a"));
        boolean cc = strs.stream().noneMatch(str -> str.equals("a"));
        long count = strs.stream().filter(str -> str.equals("a")).count();
        System.out.println(aa);// TRUE
        System.out.println(bb);// FALSE
        System.out.println(cc);// FALSE
        System.out.println(count);// 4
    }

3.2 筛选(filter)

筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。

 

Stream<T> filter(Predicate<? super T> predicate);

该接口接收一个Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。

java.util.stream.Predicate 函数式接口,其中唯一的抽象方法为:

可以看出该方法有参数,有返回值,该方法将会产生一个boolean值结果,代表指定的条件是否满足。如果结果为true,那么Stream流的filter 方法将会留用元素;如果结果为false,那么filter 方法将会舍弃元素。

案例一:筛选出Integer集合中大于7的元素,并打印出来

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
        Stream<Integer> stream = list.stream();
        stream.filter(x -> x > 7).forEach(System.out::println);
    }

执行结果:

8
9

案例二: 筛选员工中工资高于8000的人,并形成新的集合。 形成新集合依赖collect(收集)

案例中使用的员工类:

package com.hztest.domain;


public class Person {
    private String name;  // 姓名
    private int salary; // 薪资
    private int age; // 年龄
    private String sex; //性别
    private String area;  // 地区

    // 构造方法
    public Person(String name, int salary, int age,String sex,String area) {
        this.name = name;
        this.salary = salary;
        this.age = age;
        this.sex = sex;
        this.area = area;
    }
    // 省略了get和set,请自行添加
}
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

        List<String> fiterList = personList.stream().filter(x -> x.getSalary() > 8000).map(Person::getName)
                .collect(Collectors.toList());
        System.out.print("高于8000的员工姓名:" + fiterList);

    }

运行结果:

高于8000的员工姓名:[Tom, Anni, Owen]

案例3:

    public static String2BaseValEnum valueOfClazz(Class clazz){
        return Arrays.stream(String2BaseValEnum.values()).filter(string2BaseValEnum -> {
            return string2BaseValEnum.getClazz()==clazz;
        }).findFirst().orElseGet(()->{
            return String2BaseValEnum.STRING;
        });
    }

案例4:

List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
// 遍历过滤筛选符合条件的元素
List<Integer> filterList = list.stream().filter(x -> x > 6).collect(Collectors.toList());
System.out.println(filterList);

debug断点:

3.3 聚合(max/min/count)

maxmincount这些字眼你一定不陌生,没错,在mysql中我们常用它们进行数据统计。Java stream中也引入了这些概念和用法,极大地方便了我们对集合、数组的数据统计工作。

 

 案例一:获取Integer集合中的最大值。

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 9, 4, 11, 6);

        // 自然排序
        Optional<Integer> max = list.stream().max(Integer::compareTo);
        // 自定义排序
        Optional<Integer> max2 = list.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println("自然排序的最大值:" + max.get());
        System.out.println("自定义排序的最大值:" + max2.get());

    }

执行结果:

自然排序的最大值:11
自定义排序的最大值:11

案例二:计算Integer集合中大于6的元素的个数。

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);

        long count = list.stream().filter(x -> x > 6).count();
        System.out.println("list中大于6的元素个数:" + count);

    }

执行结果:

list中大于6的元素个数:4

 4. java 8的map中使用stream

Map是java中非常常用的一个集合类型,我们通常也需要去遍历Map去获取某些值,java 8引入了Stream的概念,那么我们怎么在Map中使用Stream呢?

Map有key,value还有表示key,value整体的Entry。

注意:集合主要有List,Map,Set,Collection。除了Map没有stream,其他3个(List,Set,Collection)都有stream方法

Map可以通过以下方式,间接使用流;

        Set<Map.Entry<String, String>> entries = someMap.entrySet();
        Set<String> keySet = someMap.keySet();
        Collection<String> values = someMap.values();

        Stream<Map.Entry<String, String>> entriesStream = entries.stream();
        Stream<String> valuesStream = values.stream();
        Stream<String> keysStream = keySet.stream();

具体使用案例如下:

    public static void main(String[] args) {
        Map<String, String> someMap = new HashMap<>();


        someMap.put("jack","20");
        someMap.put("bill","35");

        Set<Map.Entry<String, String>> entries = someMap.entrySet();
        Set<String> keySet = someMap.keySet();
        Collection<String> values = someMap.values();

        Stream<Map.Entry<String, String>> entriesStream = entries.stream();
        Stream<String> valuesStream = values.stream();
        Stream<String> keysStream = keySet.stream();

        //01 使用Stream获取map的key
        Optional<String> optionalName = someMap.entrySet().stream()
                .filter(e -> "20".equals(e.getValue()))
                .map(Map.Entry::getKey)
                .findFirst();

        System.out.println(optionalName.get());

        //02 使用stream获取map的value
        List<String> listAges = someMap.entrySet().stream()
                .filter(e -> e.getKey().equals("jack"))
                .map(Map.Entry::getValue)
                .collect(Collectors.toList());

        System.out.println(listAges);
    }

控制台输出:

参考文献:https://www.jianshu.com/p/2b40fd0765c3

https://blog.csdn.net/mu_wind/article/details/109516995(重点推荐)

https://blog.csdn.net/princejwg/article/details/105941009(推荐)

https://www.cnblogs.com/kesimin/p/11082900.html---推荐

posted @ 2021-09-01 17:31  雨后观山色  阅读(315)  评论(0编辑  收藏  举报