Stream是Java8中的一大亮点,支持顺序和并行聚合操作的一系列元素,为了执行计算,流operations被组合成流管道 。 流管线由源(其可以是阵列,集合,生成函数,I / O通道等)组成,零个或多个中间操作 (其将流转换成另一个流,例如
filter(Predicate)
)以及终端操作 (产生结果或副作用,如count()
或forEach(Consumer)
Stream和集合的不同点
- 没有存储。 流不是存储元素的数据结构; 相反,它通过计算操作的流水线传送诸如数据结构,阵列,生成器功能或I / O通道的源的元件。
- 功能性质。 流上的操作产生结果,但不会修改其来源。 例如,过滤从
Stream
获得的Stream会生成新的Stream
而不需要过滤的元素,而不是从源集合中删除元素。- 懒惰寻求。 许多流操作(如过滤,映射或重复删除)可以懒惰地实现,从而暴露优化的机会。 例如,“找到具有三个连续元音的第一个
String
”不需要检查所有的输入字符串。 流操作分为中间(Stream
生产)操作和终端(价值或副作用生成)操作。 中级操作总是懒惰。- 可能无限。 虽然集合的大小有限,但流不需要。 诸如
limit(n)
或findFirst()之类的limit(n)
findFirst()
可以允许无限流上的计算在有限的时间内完成。- 消耗品流的元素只能在流的一生中访问一次。 像
Iterator
一样 ,必须生成一个新流来重新访问源的相同元素。
Stream流的获取
- 从
Collection
通过stream()
和parallelStream()
方法;- 从数组通过
Arrays.stream(Object[])
;- 从流类静态工厂的方法,如
Stream.of(Object[])
,IntStream.range(int, int)
或Stream.iterate(Object, UnaryOperator)
;- 文件的行可以从
BufferedReader.lines()
获取 ;- 文件路径的流可以从
Files
中的方法获得;- 随机数流可以从
Random.ints()
获得;- JDK中其它的数据流的方法,包括
BitSet.stream()
,Pattern.splitAsStream(java.lang.CharSequence)
和JarFile.stream()
。
//1.定义Collection
List list = new ArrayList<>();
//获取一个串行流
Stream stream = list.stream();
//获取一个并行流-底层是forkJoin实现
Stream stringStream = list.parallelStream();
//2. 使用Arrays 中的 stream() 方法,将数组转成流
String[] array = new String[5];
Stream stream = Arrays.stream(array);
//3.流类静态工厂的方法
Stream stream = Stream.of(1, 2, 3, 4, 5, 6);//1 2 3 4 5 6
Stream stream = Stream.iterate(0, (x) -> x + 2).limit(6);// 0 2 4 6 8 10
Stream stream = Stream.generate(Math::random).limit(2);//随机的2个数字
//4.使用 BufferedReader.lines() 方法,从文件中将每行内容转成流
BufferedReader reader = new BufferedReader(new FileReader("D:\\milla_stream.txt"));
Stream lineStream = reader.lines();
//5.文件路径的流可以从Files中的方法获得
Stream stream = Files.list(Paths.get("D:\\"));
//6.随机数流可以从Random.ints()获得;
IntStream ints = new Random().ints();//是Stream的子类
//7. 其他类中的方法
IntStream stream1 = new BitSet().stream();//是Stream的子类
Pattern pattern = Pattern.compile(",");
Stream text = pattern.splitAsStream("mila,love");
Stream stream2 = new JarFile("D:\\milla.jar").stream();
流的中间操作
实体类
@Data
public class StudentVO {
private String username;
private String city;
private String sex;
private int age;
public StudentVO(String username, String city, String sex, int age) {
this.username = username;
this.city = city;
this.sex = sex;
this.age = age;
}
public StudentVO() {
}
}
@Data
public class TeacherVO {
private String username;
private String city;
}
创建新的流
//生程流对象
Stream stream = Stream.of(
new StudentVO("AA", "上海", "女", 22),
new StudentVO("BB", "南京", "男", 20),
new StudentVO("BB", "杭州", "女", 19),
new StudentVO("CC", "杭州", "男", 20),
new StudentVO("DD", "南京", "女", 22),
new StudentVO("DD", "上海", "女", 23),
new StudentVO("DD", "北京", "男", 22),
new StudentVO("EE", "杭州", "男", 23),
new StudentVO("FF", "上海", "女", 24));
映射-转换
- map: - 接收函数中的参数,根据业务将它变成一个新的元素
- flatMap:- 接收函数中的参数,将每个值都进行业务替换,形成一个新的流
//获取所有学生的姓名-集合
List usernameList = list.stream().map(StudentVO::getUsername).collect(Collectors.toList());
List usernameList2 = list.stream().collect(Collectors.mapping(studentVO ->
studentVO.getUsername(), Collectors.toList()));
System.out.println("学生姓名集合:" + usernameList);
System.out.println("学生姓名集合:" + usernameList2);
//数据转换
List collect1 = list.stream().flatMap((e) -> {
TeacherVO teacher = new TeacherVO();
teacher.setUsername(e.getUsername());
teacher.setCity(e.getCity());
return Stream.of(teacher);
}).collect(Collectors.toList());
System.out.println(collect1);
排序
- sorted() - 返回由此流的元素组成的流,根据自然顺序排序
- sorted(Comparator super T> comparator) - 返回由该流的元素组成的流,根据提供的 Comparator进行排序。
//按照年龄和姓名排序-正序
List sortedList = list.stream().sorted(
Comparator.comparingInt(StudentVO::getAge)
.thenComparing(StudentVO::getUsername)
.reversed()).collect(Collectors.toList());
System.out.println("按照年龄排序:" + sortedList);
筛选 - 截取
- filter(Predicate super T> predicate); - 过滤,指定过滤条件
- limit(n) - 获取n个元素
- skip(n) - 丢弃流的第一个 n元素后,返回由该流的 n元素组成的流,和 limit(n) 协作可实现分页
- distinct - 去除重复数据(根据equals() 和 hashCode()方法)
//地址在杭州的学生数量
Stream voStream = list.stream().filter(stu -> stu.getCity().equals("杭州")).distinct().limit(5).skip(2);
消费
- peek(Consumer super T> action) - 得到每个元素并对其进行操作,与map的区别不需要返回操作的元素,与forEch的区别是该方法返回流对象能继续流式操作
//中间操作不执行,只有终端操作的时候才会执行打印
list.stream().peek(stu -> {
System.out.println("学生对象: " + stu);
}).count();
流的终止操作
allMatch - 接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch - 接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
anyMatch - 接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
findFirst - 返回流中第一个元素
findAny - 返回流中的任意元素
count - 返回流中元素的总个数
max - 返回流中元素最大值
min - 返回流中元素最小值
JDK中给出的Collectors的常用操作
//获取用户的名称组成中集合
List list = people.stream().map(Person::getName).collect(Collectors.toList());
//将名称变成set返回-有序
Set set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));
//使用逗号连接符连接
String joined = things.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
//计算元算的工资总和
int total = employees.stream()
.collect(Collectors.summingInt(Employee::getSalary)));
//根据部门分组
Map> byDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
//根据部门分组并计算各个部分的工资总和
Map totalByDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));
//根据条件-满足条件的分组
Map> passingFailing =
students.stream()
.collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
常用获取对象集合操作
Stream stream = Stream.of(
new StudentVO("AA", "上海", "女", 22),
new StudentVO("BB", "南京", "男", 20),
new StudentVO("BB", "杭州", "女", 19),
new StudentVO("CC", "杭州", "男", 20),
new StudentVO("DD", "南京", "女", 22),
new StudentVO("DD", "上海", "女", 23),
new StudentVO("DD", "北京", "男", 22),
new StudentVO("EE", "杭州", "男", 23),
new StudentVO("FF", "上海", "女", 24));
//将学生对象变成集合
List list = stream.collect(Collectors.toList());
System.out.println("学生集合: " + list);
//获取所有学生的姓名-集合
List usernameList = list.stream().map(StudentVO::getUsername).collect(Collectors.toList());
List usernameList2 = list.stream().collect(Collectors.mapping(studentVO -> studentVO.getUsername(), Collectors.toList()));
System.out.println("学生姓名集合:" + usernameList);
System.out.println("学生姓名集合:" + usernameList2);
//合并学生的姓名-连接程字符串
String usernameJoin = list.stream().map(StudentVO::getUsername).collect(Collectors.joining("-"));
System.out.println("学生姓名连接: " + usernameJoin);
//地址在杭州的学生数量
Stream voStream = list.stream().filter(stu -> stu.getCity().equals("杭州")).distinct().limit(5).skip(2);
long countHz = list.stream().filter(stu -> stu.getCity().equals("杭州")).count();
System.out.println("杭州学生的个数:" + countHz);
//按照所在城市分组
Map> groupByList = list.stream().collect(Collectors.groupingBy(studentVO -> studentVO.getCity()));
System.out.println("按照城市分组:" + groupByList);
//按照年龄和姓名排序-正序
List sortedList = list.stream().sorted(Comparator.comparingInt(StudentVO::getAge).thenComparing(StudentVO::getUsername).reversed()).collect(Collectors.toList());
System.out.println("按照年龄排序:" + sortedList);
//学生姓名作为key转变成map->如果有有重复的key,需要做个取舍-否则会报错
Map usernameKey = list.stream().collect(Collectors.toMap(studentVO -> studentVO.getUsername(), studentVO -> studentVO, (one, two) -> {
//可以根据业务保留第一个元素或者是第二个元素
return one;
}));
Map usernameKey2 = list.stream().collect(Collectors.mapping(stu -> stu.getUsername(), Collectors.toMap(stu -> stu, stu -> stu, (one, two) -> one)));
System.out.println("将学生姓名作为key: " + usernameKey);
System.out.println("将学生姓名作为key: " + usernameKey2);
//根据城市分组,然后再按照学生名变成map-所有变成map的操作都需要考虑重复元素的问题
Map> collect = list.stream().collect(Collectors.groupingBy(stu -> stu.getCity(), Collectors.toMap(stu -> stu.getUsername(), studentVO -> studentVO)));
System.out.println(collect);
//数据转换
List collect1 = list.stream().flatMap((e) -> {
TeacherVO teacher = new TeacherVO();
teacher.setUsername(e.getUsername());
teacher.setCity(e.getCity());
return Stream.of(teacher);
}).collect(Collectors.toList());
System.out.println(collect1);
StudentVO studentVO = list.parallelStream().findAny().get();
System.out.println(studentVO);
//中间操作不执行,只有终端操作的时候才会执行打印
list.stream().peek(stu -> {
System.out.println("学生对象: " + stu);
}).count();