初识Java8新特性-Stream API
个人理解:Stream API 非常强大! 在Java8之后.对数据的操作非常的方便,操作数据就好像写SQL语句.
使用Stream API 需要了解到一下几点
- Stream API 在流的传输过程中,做一些流水线式的处理,产生一个新流
- Stream自己不会存储元素
- Stream不会改变源对象,返回一个持有结果的新流
- Stream操作是延时执行的
使用Stream API 需要三个步骤- 创建Stream
- 做一些流水线式的中间操作
- 终止操作
一. 下面介绍第一步,怎样创建一个Stream流
可以通过集合任意的方法 stream()或parallelStream()
Listlist = new ArrayList<>();
Streamstream1 = list.stream();
通过Arrays类的静态方法 stream() 获取数组流
Student[] students = new Student[10];
Streamstream2 = Arrays.stream(students);
通过Stream类的静态方法of(),此方法为可变参数
Streamstream3 = Stream.of(1,2,3,4);
通过Stream.iterate来创建无限流
Streamstream4 = Stream.iterate(0, (x) -> ++x);
stream4.limit(20)
.forEach(System.out::println);
通过Stream.generate来创建无限流
Streamstream5 = Stream.generate(() -> (int)(Math.random()*100));
stream5.limit(10)
.forEach(System.out::println);
二. 第二步,流水线式的中间操作
注意: 中间操作不执行任何操作,在终止操作的时候一次性执行全部操作,叫做"惰性求值"或"延迟加载"
首先我们进行一些数据准备,之后我们直接操作students流,以便看出效果
public List
new Student("John",23,"男"),
new Student("麦克",19,"女"),
new Student("肉丝",19,"女"),
new Student("肉丝",19,"女"),
new Student("肉丝",19,"女"),
new Student("肉丝",19,"女"),
new Student("肉丝",19,"女"),
new Student("肉丝",19,"女"),
new Student("肉丝",19,"女"),
new Student("肉丝",19,"女"),
new Student("露丝",19,"女")
);
中间操作—-筛选和切片
filter -> 接收 Lambda,从流中排出某些元素
limit -> 截断流,让元素不超过指定数量(当数量够的时候就不再进行迭代操作)
skip -> 跳过指定数目个元素,正好和limit方法互补
distinct-> 去除重复元素,根据hashCode和equals方法,要求元素类必须重写这两个方法
@Test
public void Test2() {
students.stream().filter((x) -> x.getAge() > 18)
.distinct()
.limit(2)
.forEach(System.out::println);//内部迭代,由 Stream API完成
}
中间操作—-映射
map:接收Lambda,将元素转换成其他形式或提取信息.接收一个函数作为参数,该函数会被应用到每一个元素上并将其映射为一个新元素.
flagMap: 接收一个函数作为参数,将流中的每一个值都转换为一个流,最后将所有流拼接成一个流
两者的区别就相当于 list中的
add() -> 什么都能加,可以出现嵌套集合 例如:{1,2,{3,4}}
和
addAll() -> 如果参数是一个集合,那么将集合中的元素加到里面 例如:{1,2,3,4}
演示map的用法 将list中的字符串转换为大写字母
List
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
效果如下:
A
BB
CCC
DDDD
EEEEE
接下来我们新建一个工具方法,以便对比map和flagMap的用法
/**
-
将一个字符串切割成单个字符 - @param str
- @return 由单个字符组成的Stream流
*/
public static StreamfilterCharacter(String str){
Listlist = new ArrayList<>();
for (Character character : str.toCharArray()) {
list.add(character);
}
return list.stream();
}
演示map的用法,将list中的字符串全部切割成单个字符
list.stream()
.map(Stream_Learn::filterCharacter)
.forEach((s) -> {
s.forEach(System.out::println);
});
注意:这种情况下:流中流,所以forEach的时候也要嵌套的forEach
下面演示flagMap的用法:避免流中流
list.stream()
.flatMap(Stream_Learn::filterCharacter)
.forEach(System.out::println);
两者的效果是一样的:
a
b
b
c
c
c
d
d
d
d
e
e
e
e
e
中间操作—-排序
sorted() 自然排序
sorted(Comparator com) 定制排序
@Test
public void Test4() {
List
//将list排序
list.stream()
.sorted()
.forEach(System.out::println);
//先按年龄对students进行排序,年龄一样再按姓名排序
students.stream()
.sorted((x,y) -> {
if(x.getAge()==y.getAge()) {
return x.getName().compareTo(y.getName());
}else {
return String.valueOf(x.getAge()).compareTo(String.valueOf(y.getAge()));
}
})
.forEach(System.out::println);
}
三. 最后一步,终止操作终止操作—-查找与匹配
终止操作—-查找与匹配
allMatch -> 检查是否匹配到所有元素
anyMatch -> 检查是否匹配到一个元素
noneMatch -> 检查是否没有匹配所有元素
findFirst -> 返回第一个元素
findAny -> 返回当前流中任意元素
count -> 返回流中的总个数
max -> 返回流中的最大值
min -> 返回六中的最小值
Optional集合解决空指针异常,如果流中得到的结果为空,则就用替补元素 -> orElse(替补元素)方法
//获得第一个学生
Optional<Student> op1 = students.stream()
.findFirst();
op1.orElse(new Student("替补学生",30,"女"));
System.out.println(op1.get());
Optional<Student> op2 = students.parallelStream()//获得并行流
.findAny();
op2.orElse(new Student("替补学生",30,"女"));
System.out.println(op2.get());
Long count = students.stream()
.count();
System.out.println("流中的个数:"+count);
//获得年龄最大的学生
Optional<Student> stu = students.stream()
.max((x,y) ->
Integer.compare(x.getAge(), y.getAge()));
System.out.println(stu.get());
//获得年龄最小的学生
Optional<Student> stu1 = students.stream()
.min((x,y) ->
Integer.compare(x.getAge(), y.getAge()));
System.out.println(stu1.get());
//获得最小的年龄
Optional<Integer> minAge = students.stream()
.map(Student::getAge)
.min(Integer::compare);
System.out.println("最小年龄:"+minAge.get());
终止操作—-归约
reduce ( T identity,BinaryOperator) / reduce(BinaryOPerator) —-可以将流中元素反复结合起来,得到一个值
@Test
public void Test6() {
//获得list的总和
List
Integer sum = list.stream()
.reduce(0, (x,y) -> x+y);
System.out.println(sum);
//获得学生的总年龄
Optional
.map(Student::getAge) //map-reduce模式
.reduce(Integer::sum);
System.out.println("学生总年龄:"+sumAge.get());
}
终止操作—-收集
collect—将流转换为其他形式.接收一个Collector接口实现,用于给Stream中元素做汇总的方法
注意: collect方法要与Collectors类配合使用
1.将学生的姓名从students集合中提取出来,并放入一个新的集合中
//提取到List中
List
.map(Student::getName)
.collect(Collectors.toList());
System.out.println(namesList);
//提取到Set中
Set
.map(Student::getName)
.collect(Collectors.toSet());
System.out.println(namesSet);
//提取到任何一个集合中(这里以HashSet为例)
HashSet
.map(Student::getName)
.collect(Collectors.toCollection(HashSet::new));
System.out.println(hs);
2.获得一些常用的数据(总数,最大值,最小值,平均值….)
方法一:
Long count = students.stream()
.collect(Collectors.counting());
System.out.println("总数:"+count);
Integer sumAge = students.stream()
.collect(Collectors.summingInt(Student::getAge));
System.out.println("年龄总和:"+sumAge);
Double avgAge = students.stream()
.collect(Collectors.averagingDouble(Student::getAge));
System.out.println("年龄的平均值:"+avgAge);
Optional
.collect(Collectors.maxBy((x,y)-> Integer.compare(x.getAge(), y.getAge())));
System.out.println("最大年龄的学生:"+stu.get());
Optional
.collect(Collectors.minBy((x,y)-> Integer.compare(x.getAge(), y.getAge())));
System.out.println("最小年龄的学生:"+stu1.get());
方法二:
DoubleSummaryStatistics dss = students.stream()
.collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("年龄的平均值"+dss.getAverage());
System.out.println("总人数"+dss.getCount());
System.out.println("年龄的最大值"+dss.getMax());
System.out.println("年龄的最小值"+dss.getMin());
System.out.println("年龄的总和"+dss.getSum());
3. 分组和多级分组以及分区
//根据性别分组
Map<String, List
.collect(Collectors.groupingBy(Student::getSex));
System.out.println(sexGroup);
结果如下:
{
女=[Student [name=麦克, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=露丝, age=19, sex=女]],
男=[Student [name=莫提, age=20, sex=男], Student [name=moti, age=18, sex=男], Student [name=John, age=23, sex=男]]
}
//多级分组,先按照性别分组,再按照年龄分组
Map<String, Map<Integer, List
.collect(Collectors.groupingBy(Student::getSex, Collectors.groupingBy(Student::getAge)));
System.out.println(duoGroup);
结果如下:
{
女={
19=[Student [name=麦克, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=露丝, age=19, sex=女]]
},
男={
18=[Student [name=moti, age=18, sex=男]],
20=[Student [name=莫提, age=20, sex=男]],
23=[Student [name=John, age=23, sex=男]]
}
}
//通过分区,将男女分开
Map<Boolean, List
.collect(Collectors.partitioningBy((s) -> "男".equals(s.getSex())));
System.out.println(boolgroup);
结果如下:
{
false=[Student [name=麦克, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=肉丝, age=19, sex=女], Student [name=露丝, age=19, sex=女]],
true=[Student [name=水寒, age=20, sex=男], Student [name=moti, age=18, sex=男], Student [name=John, age=23, sex=男]]
}
最后还有个字符键拼接的小操作
String names = students.stream()
.map(Student::getName)
.collect(Collectors.joining(",","#","#"));//也可以不加参数
System.out.println(names);
效果如下:
#水寒,moti,John,麦克,肉丝,肉丝,肉丝,肉丝,肉丝,肉丝,肉丝,肉丝,露丝#

浙公网安备 33010602011771号