Java 8 之 Stream

By | 2021年12月31日

1 User模型

public class User {
    private String address;

    private String name;

    private Integer age;

    public User(String address, String name, Integer age) {
        this.address = address;
        this.name = name;
        this.age = age;
    }

    ......

    @Override
    public String toString() {
        return "User [address=" + address + ", name=" + name + ", age=" + age + "]";
    }
}

2 获取Stream

2.1 通过of方法

User user1 = new User("上海", "甲", 17);
User user2 = new User("上海", "乙", 19);
User user3 = new User("北京", "丙", 20);
User user4 = new User("北京", "丁", 18);

Stream<User> stream = Stream.of(user1, user2, user3, user4);

2.2 通过Collection

List<User> list = new ArrayList<>();
Stream<User> stream = list.stream();

2.3 如何消费多次Stream?

提示:使用stream只能消费一次,否则报错:stream has already been operated upon or closed。

//第一次使用 stream
stream.map(user -> user.getName()).collect(Collectors.toList());
//第二次使用 stream 会报错。
stream.map(user -> user.getName()).collect(Collectors.toSet());

//解决办法
Supplier<Stream<User>> streamSupplier   = () -> Stream.of(user1, user2, user3, user4);
streamSupplier.get().map(user -> user.getName()).collect(Collectors.toList());
streamSupplier.get().map(user -> user.getName()).collect(Collectors.toSet());

3 常用

Supplier<Stream<User>> streamSupplier = () -> Stream.of(user1, user2, user3, user4);

// 转list
List<String> names = streamSupplier.get().map(user -> user.getName()).collect(Collectors.toList());
// 转set
Set<String> address = streamSupplier.get().map(user -> user.getName()).collect(Collectors.toSet());
// 转map,需要指定key和value,Function.identity()表示当前的User对象本身(转map很常用)
Map<String, User> map = streamSupplier.get().collect(Collectors.toMap(user -> user.getName(), Function.identity()));

// 计算元素中的个数
Long count = streamSupplier.get().collect(Collectors.counting());

List<String> strs = Arrays.asList("a", "a", "a", "a", "b");
//只要有一个 a,就为 true,下面返回:TRUE
boolean anyMatch = strs.stream().anyMatch(str -> str.equals("a"));
//都是 a,就为 true,下面返回:FALSE
boolean allMatch = strs.stream().allMatch(str -> str.equals("a"));
//都不是 a,就为 true,下面返回:FALSE
boolean noneMatch = strs.stream().noneMatch(str -> str.equals("a"));


// 数据求和 summingInt summingLong,summingDouble
Integer sumAges = streamSupplier.get().collect(Collectors.summingInt(User::getAge));
// 平均值 averagingInt,averagingDouble,averagingLong
Double aveAges = streamSupplier.get().collect(Collectors.averagingInt(User::getAge));

// 综合处理的,求最大值,最小值,平均值,求和操作
// summarizingInt,summarizingLong,summarizingDouble
IntSummaryStatistics intSummary = streamSupplier.get().collect(Collectors.summarizingInt(User::getAge));
System.out.println(intSummary.getAverage());  //25.0
System.out.println(intSummary.getMax());      //40
System.out.println(intSummary.getMin());      //10
System.out.println(intSummary.getSum());      //100

// 连接字符串,当然也可以使用重载的方法,加上一些前缀,后缀和中间分隔符
String str1 = streamSupplier.get().map(user -> user.getName()).collect(Collectors.joining());
String str2 = streamSupplier.get().map(user -> user.getName()).collect(Collectors.joining(","));
String str3 = streamSupplier.get().map(user -> user.getName()).collect(Collectors.joining(",", "<", ">"));
System.out.println(str1);  //甲乙丙丁
System.out.println(str2);  //甲,乙,丙,丁
System.out.println(str3);  //<甲,乙,丙,丁>

// maxBy 按照比较器中的比较结果刷选
// 最大值
Optional<Integer> maxAge = streamSupplier.get().map(user -> user.getAge()).collect(Collectors.maxBy(Comparator.comparingInt(age -> age)));
// 最小值
Optional<Integer> minAge = streamSupplier.get().map(user -> user.getAge()).collect(Collectors.minBy(Comparator.comparingInt(age -> age)));

// 归约操作
Optional<Integer> sumAge1 = streamSupplier.get().map(user -> user.getAge()).collect(Collectors.reducing((x, y) -> x + y));   //100
Object sumAge2 = streamSupplier.get().map(user -> user.getAge()).collect(Collectors.reducing(2, (x, y) -> x + y));  //102

// partitioningBy 分区操作(将大于30和小于等于30的分开)
Map<Boolean, List<User>> partitioningMap = streamSupplier.get()
        .collect(Collectors.partitioningBy(user -> user.getAge() > 30));

4 group方法

4.1 按地址分组

Map<String, List<User>> userGroups = Stream.of(user1, user2, user3, user4)
        .collect(Collectors.groupingBy(user -> user.getAddress()));

Iterator it = userGroups.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<Integer, List<User>> entry = (Map.Entry) it.next();
    System.out.println("组名称 " + entry.getKey() + " = " + entry.getValue().size() + "条");
    for (User user : entry.getValue()) {
        System.out.println("  " + user.getName());
    }
}

/*
输出:
组名称 上海 = 2条
  甲
  乙
组名称 北京 = 2条
  丙
  丁

*/

4.2 按地址分组,并返回每组年龄最大的User

Map<String, User> maxUserGroups = Stream.of(user1, user2, user3, user4)
        .collect(Collectors.groupingBy(user -> user.getAddress(),
                Collectors.collectingAndThen(
                        Collectors.maxBy(Comparator.comparingLong(user -> user.getAge())),
                        Optional::get)
        ));

Iterator it = maxUserGroups.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<Integer, User> entry = (Map.Entry) it.next();
    System.out.println("组名称:" + entry.getKey());
    System.out.println("本组最大年龄用户:" + entry.getValue());
}

/*
输出:
组名称:上海
本组最大年龄用户:User [address=上海, name=乙, age=19]
组名称:北京
本组最大年龄用户:User [address=北京, name=丙, age=20]
*/

5 排序

// 一、sorted() 默认使用自然序排序, 其中的元素必须实现Comparable 接口

Supplier<Stream<String>> stringListSupplier = () -> Stream.of("a", "e", "c", "d", "b");
// 自然序排序一个list
stringListSupplier.get().sorted();
// 自然序逆序元素,使用Comparator 提供的reverseOrder() 方法
stringListSupplier.get().sorted(Comparator.reverseOrder());

// 二、sorted(Comparator<? super T> comparator) :我们可以使用lambada 来创建一个Comparator 实例。可以按照升序或着降序来排序元素。

User user1 = new User("上海", "甲", 17);
User user2 = new User("上海", "乙", 19);
User user3 = new User("北京", "丙", 20);
User user4 = new User("北京", "丁", 18);
Supplier<Stream<User>> supplier = () -> Stream.of(user1, user2, user3, user4);
//User没有实现Comparable 接口,但可以使用Comparator 来排序
supplier.get().sorted(Comparator.comparing(User::getAge));
supplier.get().sorted(Comparator.comparing(User::getAge).reversed());

6 生成一个无限长度的Stream

6.1 使用 generate()

// 方式一
Stream.generate(() -> Math.random());
// 方式二
Stream.generate(Math::random);
// 方式三
Stream.generate(new Supplier<Double>() {
    @Override
    public Double get() {
    return Math.random();
    }
});

// 一般这种无限长度的Stream都会配合Stream的limit()方法来用,否则会一直打印下去
Stream.generate(() -> Math.random()).limit(3).forEach(c -> System.out.println(c));

6.2 使用 iterate()

// 给定一个 seed,然后无限循环
Stream.iterate(1, num -> num + 1).limit(3).forEach(System.out::println);

/*
输出结果:
1
2
3
*/

发表评论

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