【Java 8】手把手教你 Stream 类

1. Stream 概述

Stream 是 Java8 中处理集合的关键抽象概念(将要处理的集合元素看作一种流),它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

总之,Stream API 提供了一种高效且易于使用的处理数据的方式。

它的特点:

  • 它不是数据结构,所以,不会保存数据,只是按照特定的规则对数据进行计算,一般会输出结果
  • 不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中(保留意见:毕竟peek方法可以修改流中元素)
  • 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算

Stream 对流的操作分为两种:中间操作、终端操作

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

在这里插入图片描述

  • 无状态:指元素的处理不受之前元素的影响
  • 有状态:指该操作只有拿到所有元素之后才能继续下去
  • 非短路操作:指必须处理所有元素才能得到最终结果
  • 短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果

在使用 stream 之前,先理解一个概念:Optional。因为 Stream 中的元素是以 Optional 类型存在的。

2. Stream 用法

2.1 创建 Stream

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

public static void main(String[] args) {
    List<String> list = Arrays.asList("a", "b", "c");
    // 顺序流
    Stream<String> stream = list.stream();
    // 并行流
    Stream<String> stringStream = list.parallelStream();
}

stream 和 stringStream 的简单区分: stream 是顺序流,由主线程按顺序对流执行操作;而stringStream 是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求

如果流中的数据量足够大,并行流可以加快处速度。除了直接创建并行流,还可以通过 parallel() 方法把顺序流转换成并行流:

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

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

int[] array = {1, 2, 3, 4, 5};
IntStream stream1 = Arrays.stream(array);

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

// 1. of()
Stream<Integer> integerStream = Stream.of(10, 20, 30);

// 2. iterate() 通过迭代方法,生成一个有序无限的数据流。(由于是无限流,一般都是配合 limit() 方法来使用)
Stream<Integer> integerStream1 = Stream.iterate(2, x -> x + 2).limit(5);
integerStream1.forEach(System.out::println);

// 3. generate() 返回无限顺序无序流
Stream<Double> doubleStream = Stream.generate(Math::random).limit(3);
doubleStream.forEach(System.out::println);

Stream.generate(new Random()::nextInt).limit(3).forEach(System.out::println);

2.2 流的操作

后续需要使用的实体类

public class User {
    private String name;
    private int salary;
    private int age;
    private String sex;
    private String area;
	
	// getter/setter
}
public class UserUtil {

    public static List<User> userList = null;

    static {
        userList = new ArrayList<>();
        userList.add(new User("Tom", 8900, "male", "New York"));
        userList.add(new User("Jack", 7000, "male", "Washington"));
        userList.add(new User("Lily", 7800, "female", "Washington"));
        userList.add(new User("Anni", 8200, "female", "New York"));
        userList.add(new User("Owen", 9500, "male", "New York"));
        userList.add(new User("Alisa", 7900, "female", "New York"));
    }

    private UserUtil() {}

    public static List<User> getUserList() {
        return userList;
    }
}
2.2.1 filter()/forEach 过滤/遍历

1、遍历输出集合中大于 3 的值

List<Integer> integerList = Arrays.asList(1, 3, 5, 6, 9);
// 遍历输出符合条件的值
integerList.stream().filter(x -> x > 3).forEach(System.out::println);

2、将原集合大于 3 的元素赋值到新的集合中去

List<Integer> integerList = Arrays.asList(1, 3, 5, 6, 9);
// 返回一个符合条件的新集合
List<Integer> collect = integerList.stream().filter(x -> x > 3)
											.collect(Collectors.toList());
2.2.2 find() 查找
List<Integer> integerList = Arrays.asList(1, 3, 5, 6, 9);

// 1. 输出符合条件的第一个元素的值
Optional<Integer> first = integerList.stream().filter(x -> x > 3).findFirst();
System.out.println(first.get());
// 2. 输出符合条件的任何元素的值 findAny 适用于并行流
Optional<Integer> any = integerList.parallelStream().filter(x -> x > 3).findAny();
System.out.println(any.get());
// 3. 是否包含特定条件的元素
boolean b = integerList.stream().anyMatch(x -> x > 10);
System.out.println(b);
2.2.3 max() 最大值

1、获取字符串最长的对象

List<String> stringList = Arrays.asList("C++", "Java", "Go", "Python");
Optional<String> max = stringList.stream().max(Comparator.comparing(String::length));
System.out.println(max.get());

2、获取 Integer 集合中最大值的元素

List<Integer> integerList = Arrays.asList(1, 3, 5, 6, 9);
// 1. 获取最大值---自然排序
Optional<Integer> max1 = integerList.stream().max(Integer::compareTo);
System.out.println(max1.get());

// 2. 获取最大值---自定义排序
Optional<Integer> max2 = integerList.stream().max(new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1.compareTo(o2);
    }
});
System.out.println(max2.get());

3、获取员工工资最高的人

List<User> userList = UserUtil.getUserList();
Optional<User> max3 = userList.stream().max(Comparator.comparingInt(User::getSalary));
System.out.println(max3.get());
2.2.4 count() 计数

计算 Integer 集合中大于6的元素的个数

List<Integer> integerList = Arrays.asList(1, 3, 5, 6, 9);
// 获取符合特定条件的元素个数
long count = integerList.stream().filter(x -> x > 3).count();
System.out.println(count);
2.2.5 map() 映射

1、英文集合中的元素全部变为大写

List<String> stringList = Arrays.asList("C++", "Java", "Go", "Python");
List<String> collect1 = stringList.stream().map(String::toUpperCase).collect(Collectors.toList());

2、整数集合中的元素都加 3

List<Integer> integerList = Arrays.asList(1, 3, 5, 6, 9);
List<Integer> collect2 = integerList.stream().map(x -> x + 3).collect(Collectors.toList());

3、将员工的薪资加 1000

List<User> userList2 = UserUtil.getUserList();
// 1. 不改变原来的集合
List<User> userListNew = userList2.stream().map(user -> {
    User u = new User();
    u.setName(user.getName());
    u.setSalary(user.getSalary() + 1000);
    return u;
}).collect(Collectors.toList());

// 2. 改变原来的集合
List<User> userList3 = UserUtil.getUserList();
List<User> userListNew2 = userList3.stream().map(user -> {
    user.setSalary(user.getSalary() + 1000);
    return user;
}).collect(Collectors.toList());
2.2.6 reduce() 归约

归约,也称缩减,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作

1、求 Integer 集合的元素之和

List<Integer> integerList = Arrays.asList(1, 3, 5, 6, 9);
// 方式一
Optional<Integer> reduce = integerList.stream().reduce((x, y) -> x + y);
System.out.println(reduce.get());

// 方式二
Optional<Integer> reduce1 = integerList.stream().reduce(Integer::sum);
System.out.println(reduce1.get());

// 方式三
Integer reduce2 = integerList.stream().reduce(0, Integer::sum);
System.out.println(reduce2);

2、求 Integer 集合的元素之积

List<Integer> integerList = Arrays.asList(1, 3, 5, 6, 9);
Optional<Integer> reduce3 = integerList.stream().reduce((x, y) -> x * y);

3、求 Integer 集合的最大值

List<Integer> integerList = Arrays.asList(1, 3, 5, 6, 9);
// 方式一
Optional<Integer> reduce4 = integerList.stream().reduce((x, y) -> x > y ? x : y);
System.out.println(reduce4.get());

// 方式二
Integer reduce5 = integerList.stream().reduce(0, Integer::max);
System.out.println(reduce5);

4、求所有员工的工资之和

// 方式一
List<User> userList4 = UserUtil.getUserList();
Optional<Integer> reduce6 = userList4.stream().map(User::getSalary).reduce(Integer::sum);
System.out.println(reduce6.get());

// 方式二
Integer reduce8 = userList4.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);
System.out.println(reduce8);

// 方式三
userList4.stream().reduce(0, (sum, p) -> sum += p.getSalary(), (sum1, sum2) -> sum1 + sum2);

5、求所有员工的最高工资

List<User> userList5 = UserUtil.getUserList();
Integer reduce7 = userList5.stream().reduce(0, (m, p) -> m > p.getSalary() ? m : p.getSalary(), Integer::max);
System.out.println(reduce7);
2.2.7 collect() 收集

collect(),收集,可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。

collect() 主要依赖 java.util.stream.Collectors 类内置的静态方法。

2.2.7.1 toList()/toSet()/toMap() 归集

因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList、toSet 和 toMap 比较常用,另外还有 toCollection、toConcurrentMap 等复杂一些的用法。

// toList()
List<Integer> integerList = Arrays.asList(1, 3, 4, 6, 8, 9, 12);
List<Integer> collect = integerList.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
System.out.println(collect);

// toSet()
Set<Integer> collect2 = integerList.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
System.out.println(collect);

// toMap()
List<User> userList = UserUtil.getUserList();
Map<String, User> collect3 = userList.stream().filter(user -> user.getSalary() > 8000).collect(Collectors.toMap(User::getName, user -> user));
System.out.println(collect3);
2.2.7.2 count()/averaging() 计数/平均

Collectors 提供了一系列用于数据统计的静态方法:

  • 计数:count()
  • 平均值:averagingInt()、averagingLong()、averagingDouble()
  • 最值:maxBy()、minBy()
  • 求和:summingInt()、summingLong()、summingDouble()
  • 统计以上所有:summarizingInt()、summarizingLong()、summarizingDouble()

1、统计员工集合的人数

// 方式一
long count = userList.stream().count();
System.out.println(count);

// 方式二
long count2 = userList.stream().collect(Collectors.counting());

2、员工集合的平均工资

Double collect1 = userList.stream().collect(Collectors.averagingDouble(User::getSalary));
System.out.println(collect1);

3、员工集合的最高工资

Optional<Integer> collect4 = userList.stream().map(User::getSalary).collect(Collectors.maxBy(Integer::compare));
System.out.println(collect4.get());

4、员工的工资总和

Integer collect5 = userList.stream().collect(Collectors.summingInt(User::getSalary));
System.out.println(collect5);

5、统计所有信息(总数、合计、最小值、平均值、最大值)

DoubleSummaryStatistics collect6 = userList.stream().collect(Collectors.summarizingDouble(User::getSalary));
System.out.println(collect6);
2.2.8 partitioningBy()/groupingBy() 分组
  • 分区:将 Stream 按条件分为两个 Map,比如员工按薪资是否高于8000分为两部分。
  • 分组:将集合分为多个 Map,比如员工按性别分组。有单级分组和多级分组

1、将员工按薪资是否高于8000分为两部分

Map<Boolean, List<User>> collect7 = userList.stream().collect(Collectors.partitioningBy(user -> user.getSalary() > 8000));
System.out.println(collect7);

// groupingBy() 分组
Map<String, List<User>> collect8 = userList.stream().collect(Collectors.groupingBy(User::getSex));
System.out.println(collect8);

2、将员工按性别和地区分组

// groupingBy() 多级分组 先按性别分组,再按地区分组
Map<String, Map<String, List<User>>> collect9 =
        userList.stream().collect(Collectors.groupingBy(User::getSex, Collectors.groupingBy(User::getArea)));
System.out.println(collect9);
2.2.8 joining() 接合

joining() 可以将 Stream 中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串

List<String> strings = Arrays.asList("A", "B", "C");
String collect10 = strings.stream().collect(Collectors.joining("-"));
System.out.println(collect10);

String collect11 = userList.stream().map(user -> user.getName()).collect(Collectors.joining(","));
System.out.println(collect11);
2.2.9 sorted() 排序

sorted(),中间操作。有两种排序:

  • sorted():自然排序,流中元素需实现Comparable接口
  • sorted(Comparator com):Comparator 排序器自定义排序

1、将员工按工资由低到高排序(自然排序)

List<String> collect13 = userList.stream().sorted(Comparator.comparing(User::getSalary)).map(User::getName).collect(Collectors.toList());
System.out.println(collect13);

2、将员工按工资由高到低排序(reversed() 方法)

List<String> collect14 = userList.stream().sorted(Comparator.comparing(User::getSalary).reversed())
								.map(User::getName).collect(Collectors.toList());
System.out.println(collect13);

3、先按工资、再按年龄升序排序(thenComparing() 方法)

// 自然排序
List<User> collect15 = userList.stream().sorted(Comparator.comparing(User::getSalary).thenComparing(User::getAge)).collect(Collectors.toList());
System.out.println(collect15);

// 自定义排序
userList.stream().sorted((user1, user2) -> {
    if (user1.getSalary() == user2.getSalary()) {
        return user1.getAge() - user2.getAge();
    } else {
        return user2.getSalary() - user1.getSalary();
    }
}).collect(Collectors.toList());
2.2.10 提取/组合

流也可以进行合并、去重、限制、跳过等操作

String[] arr1 = {"a", "b", "c", "d", "e"};
String[] arr2 = {"f", "g", "h", "i", "g"};

// concat:合并两个流 distinct:去重
List<String> list = Stream.concat(Stream.of(arr1), Stream.of(arr2)).distinct().collect(Collectors.toList());

// limit:限制从流中获得前n个数据
List<Integer> integers = Stream.iterate(1, x -> x + 2).limit(5).collect(Collectors.toList());

// // skip:跳过前n个数据
List<Integer> integerList2 = Stream.iterate(1, x -> x + 2).skip(2).limit(5).collect(Collectors.toList());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值