Java8新特性_stream流

176 阅读6分钟

一、简介

Stream,Java JDK8的新特性,是处理集合的关键抽象概念。Stream使用一种类似于SQL语句,从数据库查询数据的直观方式,来提供一种对Java集合运算和表达的高阶抽象,让代码更高效、干净和简洁。

将要处理集合看作一种流,流可以在管道中传输,也可以在管道中处理,比如筛选、排序、聚合等。

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

操作步骤

  • 创建操作:从集合、数组、文件等数据源,获取stream流
  • 中间操作:对stream流进行筛选、排序、消费等操作
  • 最终操作:通过对stream的一系列操作,得到的结果

流类型

  • stream():为集合创建串行流。
  • parallelStream():为集合创建并行流。

特点

  • 不存储数据
  • 不修改源数据
  • 惰性求值(中间操作过程中只记录操作,不会立即执行,等到最终操作时才会一并执行)

二、创建操作

Collection的方法

继承java.util.Collection的类都有stream()方法,可以直接将集合转为stream流,例如常用的List、Set。

public static void main(String[] args) {
    // 创建一个集合对象
    List list = Lists.newArrayList();
    // stream():获取集合的顺序流
    Stream stream = list.stream();
    // parallelStream():获取集合的并行流
    Stream parallelStream = list.parallelStream();
}

Arrays的方法

java.util.Array工具类,里面有提供stream()方法,可以将对应类型的数组转为stream流。

public static void main(String[] args) {
    // 创建一个数组对象
    String[] array = new String[10];
    // stream():获取数组的顺序流
    Stream<String> stream = Arrays.stream(array);
}

Stream的静态方法

java.util.stream.Stream提供了of()、iterate()、generate()静态方法创建stream流。

public static void main(String[] args) {
    // of()创建,产生有序stream
    Stream<String> stringStream = Stream.of("hello", "world", "2020");
    // iterate()创建,产生无限连续有序stream
    Stream<Integer> integerStream = Stream.iterate(0, x -> x + 1).limit(10);
    // generate()创建,由Supplier接口的产生的无限无序stream
    Stream<Double> doubleStream = Stream.generate(Math::random).limit(5);
}

其他方法

可以通过读取文件内容、对字符串分割等方法,来获取stream流。

public static void main(String[] args) {
    try {
        // BufferedReader读取文件,将每行内容转为流
		Stream<String> lineStream = new BufferedReader(new FileReader("test.txt")).lines();
        // Files读取文件,将每行内容转为流
        Stream<String> stringStream = Files.lines(Paths.get("test.txt"));
        // 正则表达式将长字符分割成流
        Pattern pattern = Pattern.compile("\\s+");
        Stream<String> stringStream12 = pattern.splitAsStream("hello world every body");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

三、中间操作

测试案例

案例对象实体类

public class UserEntity {
    /**
     * 用户名称
     */
    private String name;

    /**
     * 用户年龄
     */
    private Integer age;

    /**
     * 用户性别:0男,1女
     */
    private Integer gender;
}

案例对象集合

public static void main(String[] args) {
    // 创建用户集合
    List<UserEntity> userList = new ArrayList<>();
    // 存储用户结合
    userList.add(new UserEntity("赵一", 18, 0));
    userList.add(new UserEntity("钱二", 17, 1));
    userList.add(new UserEntity("孙三", 19, 0));
    userList.add(new UserEntity("李四", 16, 1));
    userList.add(new UserEntity("周五", 21, 0));
    userList.add(new UserEntity("郑六", 20, 1));
}

筛选

filter:筛选

/**
 * 筛选出性别为男的用户,输出:
 * UserEntity(name=赵一, age=18, gender=0)
 * UserEntity(name=孙三, age=19, gender=0)
 * UserEntity(name=周五, age=21, gender=0)
 */
userList.stream().filter(user -> user.getGender() == 0).forEach(System.out::println);

distinct:去重

/**
 * 对用户数据进行去重,输出:
 * 只保留了一个 UserEntity(name=郑六, age=20, gender=1)
 */
userList.add(user6);
userList.stream().distinct().forEach(System.out::println);

limit:截取

/**
 * 截取前两个用户数据,输出:
 * UserEntity(name=赵一, age=18, gender=0)
 * UserEntity(name=钱二, age=17, gender=1)
 */
userList.stream().limit(2).forEach(System.out::println);

skip:跳过

/**
 * 跳过前四个用户数据,输出:
 * UserEntity(name=周五, age=21, gender=0)
 * UserEntity(name=郑六, age=20, gender=1)
 */
userList.stream().skip(4).forEach(System.out::println);

limit + skip:分页

/**
 * 每页2条数据,第二页的用户数据,输出:
 * UserEntity(name=孙三, age=19, gender=0)
 * UserEntity(name=李四, age=16, gender=1)
 */
userList.stream().skip(2).limit(2).forEach(System.out::println);

映射

map:映射成新元素

/**
 * 只获取用户的年龄数据,输出:
 * 18
 * 17
 * 19
 * 16
 * 21
 * 20
 */
userList.stream().map(UserEntity::getAge).forEach(System.out::println);

flatMap:映射成新流

// 创建班级一,添加赵一、钱二、孙三
List<UserEntity> classOne = new ArrayList<>();
classOne.add(user1);
classOne.add(user2);
classOne.add(user3);
// 创建班级二,添加李四、周五、郑六
List<UserEntity> classTwo = new ArrayList<>();
classTwo.add(user4);
classTwo.add(user5);
classTwo.add(user6);
// 创建班级集合,添加班级一和班级二数据
List<List<UserEntity>> classList = new ArrayList<>();
classList.add(classOne);
classList.add(classTwo);
/**
 * 通过班级集合,获取所有用户的年龄,输出:
 * 18
 * 17
 * 19
 * 16
 * 21
 * 20
 */
classList.stream()
    // flatMap,将班级集合Stream转为用户集合Stream
    .flatMap(classEntity -> classEntity.stream())
    // map,获取用户的年龄
    .map(UserEntity::getAge)
    .forEach(System.out::println);

排序

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

/**
 * 对用户的年龄进行排序,输出:
 * UserEntity(name=李四, age=16, gender=1)
 * UserEntity(name=钱二, age=17, gender=1)
 * UserEntity(name=赵一, age=18, gender=0)
 * UserEntity(name=孙三, age=19, gender=0)
 * UserEntity(name=郑六, age=20, gender=1)
 * UserEntity(name=周五, age=21, gender=0)
 */
userList.stream()
    .sorted(Comparator.comparingInt(UserEntity::getAge))
    .forEach(System.out::println);

消费

peek:消费元素,不给返回值

/**
 * 修改第一个用户年龄,并打印出前后年龄,输出:
 * 修改前age = 18
 * 修改后age = 10
 */
userList.stream().peek(user -> System.out.println("修改前age = " + user.getAge()))
                 .map(user -> {user.setAge(10); return user;})
                 .peek(user -> System.out.println("修改后age = " + user.getAge()))
                 .limit(1)
                 .collect(Collectors.toList());

四、最终操作

查找与匹配

allMatch:所有元素都满足条件返回true,否则false。

/**
 * 判断所有用户是否都大于18岁,输出:false
 */
boolean b1 = userList.stream().allMatch(user -> user.getAge() > 18);
System.out.println(b1);

/**
 * 判断所有用户是否都大于15岁,输出:true
 */
boolean b2 = userList.stream().allMatch(user -> user.getAge() > 15);
System.out.println(b2);

noneMatch:所有元素都不满足条件返回true,否则false。

/**
 * 判断所有用户是否都大于18岁,输出:false
 */
boolean b1 = userList.stream().noneMatch(user -> user.getAge() > 18);
System.out.println(b1);

/**
 * 判断所有用户是否都小于15岁,输出:true
 */
boolean b2 = userList.stream().noneMatch(user -> user.getAge() < 15);
System.out.println(b2);

anyMatch:有元素满足条件返回true,否则false

/**
 * 判断所有用户是否都大于18岁,输出:true
 */
boolean b1 = userList.stream().anyMatch(user -> user.getAge() > 18);
System.out.println(b1);

/**
 * 判断所有用户是否都小于15岁,输出:false
 */
boolean b2 = userList.stream().anyMatch(user -> user.getAge() < 15);
System.out.println(b2);

findFirst:返回流中第一个元素

/**
 * 根据用户集合获取第一个用户,输出:
 * UserEntity(name=赵一, age=18, gender=0)
 */
UserEntity userEntity = userList.stream().findFirst().get();
System.out.println(userEntity);

findAny:为了更高效的性能,返回流中的任意元素

串行:返回第一个元素;

并行:返回任意元素。

/**
 * 根据用户集合获取随机用户,输出:
 * UserEntity(name=赵一, age=18, gender=0)
 * UserEntity(name=李四, age=16, gender=1)
 */
UserEntity userEntity = userList.stream().findAny().get();
System.out.println(userEntity);
UserEntity userentity2 = userList.parallelStream().findAny().get();
System.out.println(userentity2);

count:返回流中元素的总个数

/**
 * 统计用户数量,输出:6
 */
long count = userList.stream().count();
System.out.println(count);

max:返回流中元素最大值

/**
 * 输出用户年龄最大的数据,输出:
 * UserEntity(name=周五, age=21, gender=0)
 */
UserEntity userEntity = userList.stream().max(Comparator.comparingInt(UserEntity::getAge)).get();
System.out.println(userEntity);

min:返回流中元素最小值

/**
 * 输出用户年龄最大的数据,输出:
 * UserEntity(name=李四, age=16, gender=1)
 */
UserEntity userEntity = userList.stream().min(Comparator.comparingInt(UserEntity::getAge)).get();
System.out.println(userEntity);

规约

reduce:根据计算模型将流中元素进行计算并返回结果

Optional reduce(BinaryOperator accumulator)

根据accumulator函数将流中元素进行计算。

/**
 * 统计用户年龄的综合,输出:111
 */
Optional<Integer> reduce = userList.stream().map(UserEntity::getAge).reduce(Integer::sum);
System.out.println(reduce.get());

T reduce(T identity, BinaryOperator accumulator)

插入默认元素identity,根据accumulator函数将所有元素进行计算。

/**
 * 添加个用户牛九user
 * 统计用户年龄的综合,输出:121
 */
UserEntity user = new UserEntity("牛九", 10, 0);
Integer reduce = userList.stream().map(UserEntity::getAge).reduce(user.getAge(), Integer::sum);
System.out.println(reduce);

U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator combiner)

串行:根据accumulator函数将流中元素进行计算,combiner函数没有作用。

并行:通过fork join多线程进行执行,根据accumulator函数将流中元素进行计算,得出结果生成新流在根据combiner函数计算,得出结果。

/**
 * 添加个用户牛九user
 * 串行流,比较年龄最大的用户,输出:21
 */
UserEntity user = new UserEntity("牛九", 10, 0);
Integer reduce = userList.stream().map(UserEntity::getAge).reduce(user.getAge(), Integer::max, Integer::sum);
System.out.println(reduce);

/**
 * 并行流,比较年龄最大的用户并将年龄相加,输出:111
 */
Integer reduce2 = userList.parallelStream().map(UserEntity::getAge).reduce(user.getAge(), Integer::max, Integer::sum);
System.out.println(reduce2);

收集

collect:将流中元素收集成其他数据结构

集合 :将操作过的流保存为集合对象

/**
 * 集合:转为list
 */
List<String> nameList = userList.stream().map(UserEntity::getName).collect(Collectors.toList());

/**
 * 集合:转为Set
 */
Set<String> nameSet = userList.stream().map(UserEntity::getName).collect(Collectors.toSet());

/**
 * 集合:转为map
 */
Map<String, Integer> userMap = userList.stream().collect(Collectors.toMap(UserEntity::getName, UserEntity::getAge));

聚合:统计分析出流的总数、平均值等参数

/**
 * 聚合:统计用户人数,输出 6
 */
Long count = userList.stream().collect(Collectors.counting());

/**
 * 聚合:获得用户最大年龄,输出 21
 */
Integer maxAge = userList.stream().map(UserEntity::getAge).collect(Collectors.maxBy(Integer::compareTo)).get();

/**
 * 聚合:获得用户最小年龄,输出 16
 */
Integer minAge = userList.stream().map(UserEntity::getAge).collect(Collectors.minBy(Integer::compareTo)).get();

/**
 * 聚合:统计所有人的年龄 111
 */
Integer sumAge = userList.stream().collect(Collectors.summingInt(UserEntity::getAge));

/**
 * 聚合:获得上述所有功能
 */
IntSummaryStatistics collect = userList.stream().collect(Collectors.summarizingInt(UserEntity::getAge));

分组:根据流元素的参数进行分类,返回值为Map

/**
 * 分组:将用户分成男生和女生,输出
 * {
 *  0=[UserEntity(name=赵一, age=18, gender=0), UserEntity(name=孙三, age=19, gender=0), UserEntity(name=周五, age=21, gender=0)], 
 *  1=[UserEntity(name=钱二, age=17, gender=1), UserEntity(name=李四, age=16, gender=1), UserEntity(name=郑六, age=20, gender=1)]
 * }
 */
Map<Integer, List<UserEntity>> collect = userList.stream().collect(Collectors.groupingBy(UserEntity::getGender));
System.out.println(collect);

分区:根据判断条件将流元素进行分类,返回值为Map

/**
 * 分组:以18岁年龄为界限,分为成年人和未成年人组,输出
 * {
 *  false=[UserEntity(name=赵一, age=18, gender=0), UserEntity(name=钱二, age=17, gender=1), UserEntity(name=李四, age=16, gender=1)],
 *  true=[UserEntity(name=孙三, age=19, gender=0), UserEntity(name=周五, age=21, gender=0), UserEntity(name=郑六, age=20, gender=1)]
 *  }
 */
Map<Boolean, List<UserEntity>> collect = userList.stream().collect(Collectors.partitioningBy(user -> user.getAge() > 18));
System.out.println(collect);

规约:同上诉的规约操作,将流元素根据计算模型进行计算

/**
 * 规约,统计所有用户年龄,输出
 * Optional[111]
 */
Optional<Integer> collect = userList.stream().map(UserEntity::getAge).collect(Collectors.reducing(Integer::sum));
System.out.println(collect);