一、简介
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);