Stream流
什么是Stream?
- 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
-
Stream自己不会存储元素(流是基于数据源的对象,它本身不存储数据元素,而是通过管道将数据源的元素传递给操作。)
-
Stream不会改变源对象。相反,他们会返回一个持有结果的Stream。
-
3.Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
流的三大操作
1.创建Stream流
2.通过Stream流对象执行中间操作
3.终端操作会从流的流水线生成结果
1.创建Stream
最常用的直接是集合创建流
/**
* 创建流
*/
@Test
public void testCreateStream() {
/*
集合流
- Collection.stream() 串行流
- Collection.parallelStream() 并行流
*/
Stream<Student> stream = studentList.stream();
Stream<Student> parallelStream = studentList.parallelStream();
// 从数组创建
IntStream stream3 = Arrays.stream(new int[]{2, 3, 5});
//Stream 静态方法
//Stream.of(...)
Stream<Integer> stream4 = Stream.of(1, 2, 3);
}
2.中间操作
- 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
/**
* 中间操作
*/
@Test
public void testStreamFeatures() {
//创建流
Stream<Student> stream = studentList.stream();
//过滤年龄小于15的
Stream<Student> ageStream = stream.filter(stu -> stu.getAge() > 15);
//使用map得到学生姓名,并打印学生信息
Stream<String> nameStream = ageStream.map(stu -> {
System.out.println(stu);
return stu.getName();
});
//执行终止操作
List<String> stuNameList = nameStream.collect(Collectors.toList());
}
常用的中间操作
方法 | 描述 |
---|---|
filter(Predicate predicate) | 接收一个函数作为参数,根据提供的筛选条件predicate进行过滤 |
map(Function f) | 接收一个函数作为参数,该函数会作用到每个元素上,映射成新的元素 |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都转换成另外一个流,然后所有流连接为一个流 |
sorted() | 按照自然顺序进行排序,返回一个新流 |
sorted(Comparator comp) | 自定义排序顺序,返回一个新流 |
skip(long n) | 将前n个元素进行丢弃,返回一个新流 |
limit(long maxSize) | 截取到maSize个数的元素值,返回一个新流 |
/**
* 中间操作 map
*/
@Test
public void mapTest() {
Stream<Student> studentStream = studentList
.stream();
//提取学生的姓名 将Student类型转换String类型
Stream<String> stringStream = studentList
.stream()
.map(Student::getName);
//打印输出
stringStream.forEach(System.out::println);
}
@Test
public void testFlatMap() {
//创建一个字符串集合
List<String> list = Arrays.asList("AB", "CD", "EFG");
Stream<Character> stream1= list
.stream()
.flatMap(this::stringToChar);
//打印
System.out.println("入参是String,出参是Stream<Character>:");
stream1.forEach(System.out::println);
}
@Test
public void testFilter(){
//打印过滤前的学生集合长度
System.out.println("过滤前学生长度:"+studentList.size());
//过滤年龄小于16岁的学生
List<Student> filterList = studentList
.stream()
.filter(stu -> stu.getAge() > 15)
.collect(Collectors.toList());
//打印过滤后的学生集合长度
System.out.println("过滤前学生长度:"+filterList.size());
System.out.println("过滤后,源数据源的长度:"+studentList.size());
}
@Test
public void sortedTest() {
//学生按照年龄从小到大的排序
System.out.println("按照年龄从小到大排序:");
studentList
.stream()
.sorted(Comparator.comparing(Student::getAge))
.forEach(System.out::println);
//学生按照年龄从大到小的排序
System.out.println("按照年龄从大到小排序:");
studentList
.stream()
.sorted((stu1,stu2)->stu2.getAge().compareTo(stu1.getAge()))
.forEach(System.out::println);
}
@Test
public void skipTest() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
list
.stream()
.skip(3)
.forEach(System.out::println);
}
@Test
public void limitTest() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
list
.stream()
.limit(3)
.forEach(System.out::println);
}
3.终止操作
终止在从流的流水线中(中间操作)生成最终的结果,其结果可以是任何不是流的值,且执行过终止操作的流是不可再使用的
方法 | 简述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate ) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | stream API 使用内部迭代(默认做了外部迭代) |
@Test
public void testTermination() {
//allMatch 检查是否匹配所有元素
boolean allMatch = studentList
.stream()
.allMatch(e -> e.getName().equals("张三"));
System.out.println("allMatch:" + allMatch);
boolean anyMatch = studentList
.stream()
.anyMatch(e -> e.getName().equals("李四"));
System.out.println("anyMatch:" + anyMatch);
//noneMatch 检查是否没有匹配元素
boolean noneMatch = studentList
.stream()
.noneMatch(e -> e.getName().equals("java"));
System.out.println("noneMatch:" + noneMatch);
//findFirst 返回第一个元素
Optional<Student> findAny = studentList
.stream()
.findAny();
System.out.println("findAny:" + findAny);
//findAny 返回流数据中的任意元素
Optional<Student> findFirst = studentList
.stream()
.findFirst();
System.out.println("findFirst:" + findFirst);
//count 返回流中总个数
long count = studentList
.stream()
.count();
System.out.println("count:" + count);
//max 返回流中最大的一个值
Optional<Student> max = studentList
.stream()
.max(Comparator.comparing(Student::getAge));
System.out.println("max:" + max);
//min 返回流中最小值
Optional<Student> min = studentList.stream().min(Comparator.comparing(Student::getAge));
System.out.println("min:" + max);
}
方法 | 简述 |
---|---|
reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional |
方法1返回的是一个对象T,而方法二返回的是Optional,原因是方法1给了一个初始值iden
@Test
public void test3() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Integer reduce = list.stream()
.reduce(45, Integer::sum);
Optional<Integer> reduce1 = list
.stream()
.reduce(Integer::sum);
}
- 收集器
方法 | 简述 |
---|---|
collect | 将流转换为其他形式。接收一个Collection接口的实现,用于Stream中元素做汇总的方法 |
collect常与Collectors进行搭配使用,Collectors中提供了大量的方法可以使用,详见下面表(只是常用的):
方法 | 简述 |
---|---|
Collector<T, ?, List> toList | 它将输入元素 List到一个新的 List 。 |
Collector<T, ?, Set> toSet | 将输入元素 Set到一个新的 Set 。 |
Collector<T, ?, Map<K,U>> toMap | 它将元素累加到一个 Map ,其键和值是将所提供的映射函数应用于输入元素的结果。 |
Collector<T, ?, C> toCollection | 按照遇到的顺序将输入元素累加到一个新的 Collection中 |
Collector<CharSequence, ?, String> joining | 返回一个 Collector ,将输入元素连接到一个 String ,按照顺序。 |
groupingBy | 返回Collector “由基团”上的类型的输入元件操作实现T ,根据分类功能分组元素,并且在返回的结果Map 。 |
- toList
@Test
public void toListTest(){
//跳过2个学生,收集一个新的学生集合
List<Student> students = studentList
.stream()
.skip(2)
.collect(Collectors.toList());
//打印学生信息
students.stream().forEach(System.out::println);
}
- toSet
@Test
public void toSetTest() {
//收集学生的年龄 转换为set
Set<Integer> ageSet = studentList
.stream()
.map(Student::getAge)
.collect(Collectors.toSet());
//打印年龄
ageSet.forEach(System.out::println);
}
-
tomap (F标识Function,BO标识BinaryOperator)
-
Collector toMap(F keyMapper,F valueMapper)
分别传入keyMapper、valueMapper的函数式方法,将执行后的结果放入Map中
@Test public void toMapTest() { //按照姓名转map Map<String, Integer> nameMap = studentList .stream() .collect( Collectors.toMap(Student::getName, Student::getAge) ); for (Map.Entry<String, Integer> map : nameMap.entrySet()) { System.out.println(map.getKey()+","+map.getValue()); } }
-
Collector toMap(F keyMapper,F valueMapper, BO mergeFunction)
分别传入keyMapper、valueMapper的函数式方法,将执行后的结果放入Map中,其中如果出现key值重复,则使用mergeFunction进行处理
@Test public void toMapTest() { //使用学生的年龄作为key值,key重复则保留第一个 Map<Integer, String> ageMap = studentList .stream() .collect( Collectors.toMap(Student::getAge, Student::getName, (v1, v2) -> v1) ); //打印map值 for (Map.Entry<Integer, String> map : ageMap.entrySet()) { System.out.println(map.getKey()+","+map.getValue()); } }
-
Collector toMap(F keyMapper, F valueMapper,BO mergeFunction,Supplier mapSupplier)
这个方法相比于上面方法多了一个mapSupplier,它可以用于选择哪个Map
@Test public void toMapTest() { LinkedHashMap<Integer, String> linkedHashMap = studentList .stream() .collect( Collectors.toMap(Student::getAge, Student::getName, (v1, v2) -> v2, LinkedHashMap::new) ); TreeMap<Integer, String> treeMap = studentList .stream() .collect( Collectors.toMap(Student::getAge, Student::getName, (v1, v2) -> v2, TreeMap::new) ); }
@Test public void joinTest(){ String str = studentList .stream() .map(Student::getName) //将 、 加入到每个元素中间 .collect(Collectors.joining("、")); System.out.println(str); }
-
-
groupingBy
根据某个属性值,对流进行分组,它有3个不同的方法:
-
groupingByConcurrent(Function)
基于传入的function执行得到相应的key,基于这个key值进行分组
-
groupingBy(Function, Collector)
基于传入的function执行得到相应的key,基于这个key值进行分组,collector则是基于已经分好组的value,再次做一些操作
-
groupingBy(Function, Supplier, Collector)
这个相比于上面方法,多了一个Supplier,它表示你分组使用什么样的容器存储,默认是HashMap
public void groupByTest() { //按照学生姓名进行分组 Map<String, List<Student>> nameMap = studentList .stream() .collect( //groupingByConcurrent(Function) Collectors.groupingBy(Student::getName)); //按照学生年龄进行分组.然后再统计每个组的个数 Map<Integer, Long> ageCountMap = studentList .stream() .collect( // groupingBy(Function, Collector) Collectors.groupingBy(Student::getAge, Collectors.counting())); //按照学生年龄进行分组.然后再统计每个组的个数 使用TreeMap进行存储 TreeMap<Integer, Long> ageCountTreeMap = studentList .stream() .collect( // groupingBy(Function, Collector) Collectors.groupingBy(Student::getAge, TreeMap::new, Collectors.counting()) ); for (Map.Entry<Integer, Long> integerLongEntry : ageCountMap.entrySet()) { System.out.println(integerLongEntry.getKey()+","+integerLongE ntry.getValue()); } } public void groupByTest() { //按照学生姓名进行分组 Map<String, List<Student>> nameMap = studentList .stream() .collect( //groupingByConcurrent(Function) Collectors.groupingBy(Student::getName)); //按照学生年龄进行分组.然后再统计每个组的个数 Map<Integer, Long> ageCountMap = studentList .stream() .collect(Collectors.groupingBy(Student::getAge, Collectors.counting())); //按照学生年龄进行分组.然后再统计每个组的个数 使用TreeMap进行存储 TreeMap<Integer, Long> ageCountTreeMap = studentList .stream() .collect( // groupingBy(Function, Collector) Collectors.groupingBy(Student::getAge, TreeMap::new, Collectors.counting()) ); for (Map.Entry<Integer, Long> integerLongEntry : ageCountMap.entrySet()) { System.out.println(integerLongEntry.getKey()+","+integerLongE ntry.getValue()); } }
-