java8新特性—Lambda表达式
java8新特性—方法引用
java8新特性—构造器引用
java8新特性—数组引用
个人理解:Stream API 非常强大! 在Java8之后.对数据的操作非常的方便,操作数据就好像写SQL语句.
使用Stream API 需要了解到一下几点
* Stream API 在流的传输过程中,做一些流水线式的处理,产生一个新流
* Stream自己不会存储元素
* Stream不会改变源对象,返回一个持有结果的新流
* Stream操作是延时执行的
使用Stream API 需要三个步骤
1. 创建Stream
2. 做一些流水线式的中间操作
3. 终止操作
一. 下面介绍第一步,怎样创建一个Stream流
- 1.可以通过集合任意的方法 stream()或parallelStream()
List<Student> list = new ArrayList<>();
Stream<Student> stream1 = list.stream();
- 2.通过Arrays类的静态方法 stream() 获取数组流
Student[] students = new Student[10];
Stream<Student> stream2 = Arrays.stream(students);
- 3.通过Stream类的静态方法of(),此方法为可变参数
Stream<Integer> stream3 = Stream.of(1,2,3,4);
- 4.通过Stream.iterate来创建无限流
Stream<Integer> stream4 = Stream.iterate(0, (x) -> ++x);
stream4.limit(20)
.forEach(System.out::println);
- 5.通过Stream.generate来创建无限流
Stream<Integer> stream5 = Stream.generate(() -> (int)(Math.random()*100));
stream5.limit(10)
.forEach(System.out::println);
二. 第二步,流水线式的中间操作
注意:
中间操作不执行任何操作,在终止操作的时候一次性执行全部操作,叫做"惰性求值"或"延迟加载"
首先我们进行一些数据准备,之后我们直接操作students流,以便看出效果
public List<Student> students = Arrays.asList(
new Student("莫提",20,"男"),
new Student("moti",18,"男"),
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<String> list = Arrays.asList("a","bb","ccc","dddd","eeeee");
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
效果如下:
A
BB
CCC
DDDD
EEEEE
接下来我们新建一个工具方法,以便对比map和flagMap的用法
/**
* 将一个字符串切割成单个字符
* @param str
* @return 由单个字符组成的Stream流
*/
public static Stream<Character> filterCharacter(String str){
List<Character> list = 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<Integer> list = Arrays.asList(1,3,5,2,4,7,6);
//将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 -> 返回六中的最小值
boolean b1 = students.stream()
.allMatch((stu) -> {
return "莫提".equals(stu.getName());
});
System.out.println("是否所有学生都叫莫提"+b1);//返回值为false
boolean b2 = students.stream()
.anyMatch((stu) -> {
return "莫提".equals(stu.getName());
});
System.out.println("是否有的学生都叫莫提"+b2);//结果为true
boolean b3 = students.stream()
.noneMatch((stu) -> {
return "莫提".equals(stu.getName());
});
System.out.println("是不是没有人叫莫提"+b3);//结果为false,双重否定表肯定
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> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0, (x,y) -> x+y);
System.out.println(sum);
//获得学生的总年龄
Optional<Integer> sumAge = students.stream()
.map(Student::getAge) //map-reduce模式
.reduce(Integer::sum);
System.out.println("学生总年龄:"+sumAge.get());
}
终止操作—收集
-
collect–将流转换为其他形式.接收一个Collector接口实现,用于给Stream中元素做汇总的方法
注意: collect方法要与Collectors类配合使用
-
1.将学生的姓名从students集合中提取出来,并放入一个新的集合中
//提取到List中
List<String> namesList = students.stream()
.map(Student::getName)
.collect(Collectors.toList());
System.out.println(namesList);
//提取到Set中
Set<String> namesSet = students.stream()
.map(Student::getName)
.collect(Collectors.toSet());
System.out.println(namesSet);
//提取到任何一个集合中(这里以HashSet为例)
HashSet<String> hs = students.stream()
.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<Student> stu = students.stream()
.collect(Collectors.maxBy((x,y)-> Integer.compare(x.getAge(), y.getAge())));
System.out.println("最大年龄的学生:"+stu.get());
Optional<Student> stu1 = students.stream()
.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());
- 2.分组和多级分组以及分区
//根据性别分组
Map<String, List<Student>> sexGroup = students.stream()
.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<Student>>> duoGroup = students.stream()
.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<Student>> boolgroup = students.stream()
.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,麦克,肉丝,肉丝,肉丝,肉丝,肉丝,肉丝,肉丝,肉丝,露丝#