特点
- 集合的流式操作,不是一个数据结构,不负责任何的数据存储;
- 它更像是一个迭代器,我们可以有序的获取到数据源中的每一个数据,并且可以对这些数据进行一些操作;
- 流式操作的每一个方法,返回值都是返回流的本身。
步骤
- 获取数据源:数据源可以是集合、数组 …
- 对数据进行处理:过滤、排序、映射 … (中间操作)
- 对流中数据进行整合:转成集合、数量 … (最终操作)
一、最终操作
讲解
1.准备一个实体类,注意这个实体的setter方法都是有返回值的
public class Person {
private String name;
private int age;
private int score;
public String getName() {
return name;
}
public Person setName(String name) {
this.name = name;
return this;
}
public int getAge() {
return age;
}
public Person setAge(int age) {
this.age = age;
return this;
}
public int getScore() {
return score;
}
public Person setScore(int score) {
this.score = score;
return this;
}
public Person() {
}
public Person(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
2.准备一个存储数据的类,需要使用数据时通过getData()方法获取
public class Data {
public static ArrayList<Person> getData() {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("xiaoming", 10, 100));
list.add(new Person("xiaoli", 12, 90));
list.add(new Person("xiaowang", 9, 80));
list.add(new Person("xiaozhang", 11, 70));
list.add(new Person("xiaomei", 7, 95));
list.add(new Person("xiaohei", 13, 60));
list.add(new Person("xiaohei", 13, 60));
return list;
}
}
3.使用类
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Program {
public static void main(String[] args) {
//获取数据源
Stream<Person> s = Data.getData().stream();
//Stream.of(Data.getData());
//最终操作1:collect
//Collectors工具类
//转成List
//List<Person> list = s.collect(Collectors.toList());
//System.out.println(list);
//转成Set
//Set<Person> set = s.collect(Collectors.toSet());
//System.out.println(set);
//转成Map
//Map<String, Integer> map = s.collect(Collectors.toMap(ele -> ele.getName(), ele -> ele.getScore()));
//Map<String, Integer> map = s.collect(Collectors.toMap(Person::getName, Person::getScore));
//System.out.println(map);
//最终操作2:reduce
//reduce的思想是n1+n2得到的和再与n3相加,相加之后得到的值再与n4相加,以此类推直到最后一个数据相加完毕
//Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//Optional<Integer> ret1 = s1.reduce((n1, n2) -> n1 + n2); //对若干个数字求和
//System.out.println(ret1.get());
//求Person中的总成绩
//Person tmp = new Person();//作为一个中间值,两个数求和的结果存在这个临时对象中
//Optional<Person> ret = s.reduce((n1, n2) -> tmp.setScore(n1.getScore() + n2.getScore()));
//System.out.println(ret.get().getScore());
//最终操作3: max 和 min
//需求1:找到集合中成绩最高的人的信息
//Person max = s.max((ele1, ele2) -> ele1.getScore() - ele2.getScore()).get();
//System.out.println(max);
//需求2:找到集合中年龄最小的人的信息
//Person min = s.min((ele1, ele2) -> ele1.getAge() - ele2.getAge()).get();
//System.out.println(min);
//最终操作4: match(anyMatch、allMatch、noneMatch)
//需求1:判断集合中是否包含成绩不合格的学员
//boolean ret = s.anyMatch(ele -> ele.getScore() < 60);
//System.out.println(ret);
//需求2:判断集合中是否包含成绩大于80的学员
//boolean ret = s.anyMatch(ele -> ele.getScore() > 80);
//System.out.println(ret);
//需求3:判断集合中是否所有的学员都及格
//boolean ret = s.allMatch(ele -> ele.getScore() > 60);
//System.out.println(ret);
//boolean ret = s.noneMatch(ele -> ele.getScore() < 60);//集合中不包含小于60的学员则返回true
//System.out.println(ret);
//最终操作5:count
//需求:求元数据中有多少个元素
//long count = s.count();
//System.out.println(count);
//最终操作6:forEach
//s.forEach(ele -> System.out.println(ele));
s.forEach(System.out::println);
}
}
注意事项
一个 Stream 只可以使用一次,之所以叫最终操作是因为流在使用一次后就会关闭,关闭之后就不能再使用这个流进行其他的操作了。
二、中间操作
讲解
1.Person实体类改动:
(1)重写两个方法:equals()和hashCode(),用于去重方法的验证;
(2)实现Comparable接口中的compareTo方法,来实现一个自定义排序的依据,用于排序方法的验证。
public class Person implements Comparable<Person> {
private String name;
private int age;
private int score;
public String getName() {
return name;
}
public Person setName(String name) {
this.name = name;
return this;
}
public int getAge() {
return age;
}
public Person setAge(int age) {
this.age = age;
return this;
}
public int getScore() {
return score;
}
public Person setScore(int score) {
this.score = score;
return this;
}
public Person() {
}
public Person(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
score == person.score &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, score);
}
@Override
public int compareTo(Person o) {
return this.getScore() - o.getScore(); //按照成绩进行一个排序
}
}
2.使用类
public class Process {
public static void main(String[] args) {
//中间操作1:filter
//filter是一个过滤器,可以自定义一个过滤条件,将流中满足条件的元素保留
//它返回的是一个Stream,所以拿到结果后还可以在进行其他操作
//需求:保留这个集合中成绩大于等于的学员信息
Data.getData().stream().filter(ele -> ele.getScore() >= 80).forEach(System.out::println);
//中间操作2:distinct
//distinct:去重,去除集合中重复的元素
//去重规则:
// 1.先判断hashCode()
// 2.如果hashCode相同,再判断equals()
// 3.如果先判断hashCode()和equals()返回都是true,才会去重成功
Data.getData().stream().distinct().forEach(System.out::println);
//中间操作3:sorted
//sorted:排序,对流中的元素进行排序
//sorted()无参使用,要求流中的元素对应的类需要实现 Comparable 接口,
//并且去实现Comparable中的compareTo方法,来实现一个自定义排序的依据或者大小比较的依据
Data.getData().stream().sorted().forEach(System.out::println);
//sorted(Comparator<T> c):使用自定义的规则来进行排序。在实现Comparable情况下,优先使用有参的这个排序
Data.getData().stream().sorted((ele1, ele2) -> ele1.getAge() - ele2.getAge()).forEach(System.out::println);
//中间操作4:limit
//limit:限制,只取流中前指定位的元素
Data.getData().stream().limit(3).forEach(System.out::println);//获取前三个元素
//中间操作5:skip
//skip:跳过
Data.getData().stream().skip(3).forEach(System.out::println);//跳过前三个元素
//一般情况下limit和skip是配合使用的,limit和skip返回的都是Stream
Data.getData().stream().skip(1).limit(4).forEach(System.out::println);//获取第二个到第五个元素
//中间操作6:map
//map:元素映射,用指定的元素来替换掉流中的元素
//需求1:将流中的Person对象替换成他们的姓名
Data.getData().stream().map(ele -> ele.getName()).forEach(System.out::println);
//需求2:将流中成绩小于80的Person对象替换成他们的姓名
Data.getData().stream().map(ele -> ele.getScore() >= 80 ? ele : ele.getName()).forEach(System.out::println);
//中间操作7:flatMap
String[] array = {"hello", "world"};
//需求:获取所有的字符:List -> h,e,l,l,o,w,o,r,l,d
System.out.println(Arrays.stream(array).map(ele -> ele.split("")).flatMap(Arrays::stream).collect(Collectors.toList()));
}
}
三、综合运用
import java.util.List;
import java.util.stream.Collectors;
public class Exercise {
public static void main(String[] args) {
/**
* 需求:一个集合中存储了若干个Person对象,要求查询出以下结果:
*
* 1.所有几个的学生信息
* 2.所有及格的学生姓名
* 3.班级的前3名(按照成绩)
* 4.班级的3-6名(按照成绩)
* 5.所有不及格的学生的平均成绩
* 6.将及格的学生,按照成绩降序输出所有信息
* 7.班级学生的总分
*/
// 1.所有几个的学生信息
List<Person> list1 = Data.getData().stream().filter(ele -> ele.getScore() >= 60).collect(Collectors.toList());
// 2.所有及格的学生姓名
List<String> names = Data.getData().stream().filter(ele -> ele.getScore() >= 60).map(ele -> ele.getName()).collect(Collectors.toList());
// 3.班级的前3名(按照成绩)
List<Person> list2 = Data.getData().stream().sorted((ele1, ele2) -> ele2.getScore() - ele1.getScore()).limit(3).collect(Collectors.toList());
// 4.班级的3-6名(按照成绩)
List<Person> list3 = Data.getData().stream().sorted((ele1, ele2) -> ele2.getScore() - ele1.getScore()).skip(2).limit(4).collect(Collectors.toList());
// 5.所有不及格的学生的平均成绩
Person tmp = new Person();
Data.getData().stream().filter(ele -> ele.getScore() < 60).reduce((ele1, ele2) -> tmp.setScore(ele1.getScore() + ele2.getScore()));
long count = Data.getData().stream().filter(ele -> ele.getScore() < 60).count();
float ave = tmp.getScore() / (float) count;
// 6.将及格的学生,按照成绩降序输出所有信息
Data.getData().stream().filter(ele -> ele.getScore() >= 60).sorted((ele1, ele2) -> ele2.getScore() - ele1.getScore()).forEach(System.out::println);
// 7.班级学生的总分
Data.getData().stream().reduce((ele1, ele2) -> tmp.setScore(ele1.getScore() + ele2.getScore()));
System.out.println(tmp.getScore());
}
}
四、并行流
什么是并行流?
并行流可以把流中的一些数据分成若干个块,然后用不同的线程来操作不同块的数据,并且并行流已经对我们的临界资源做好了一些处理,我们基本上不用担心多个线程访问相同资源需要面临的问题。
为什么要去使用并行流呢?
并行流由于引用了线程,我们可以进行并发的处理,从而提高一些效率和节省一些时间。
import java.util.stream.LongStream;
public class ParallelStream {
public static void main(String[] args) {
//获取并行流的方式
//Data.getData().stream().parallel()
//Data.getData().parallelStream()
//获取一个LongStream,这里边存储了若干个long型数据
//LongStream.rangeClosed(0L, 10L);//11个数据
//LongStream.rangeClosed(0L, 10L).forEach(System.out::println);
//串行流
//串行大概耗时 19661 ms
long start = System.currentTimeMillis();
LongStream.rangeClosed(0L, 50000000000L).reduce(Long::sum);//求和
long end = System.currentTimeMillis();
System.out.println("串行耗时:" + (end - start) + " ms");
//并行流
//并行大概耗时 9100 ms
long start2 = System.currentTimeMillis();
LongStream.rangeClosed(0L, 50000000000L).parallel().reduce(Long::sum);//求和
long end2 = System.currentTimeMillis();
System.out.println("并行耗时:" + (end2 - start2) + " ms");
}
}
并行流对于处理一些庞大的数据它所耗费的时间是比较短的,使用并行流一定程度上节省了运行时间。
有了并行流的概念我们再补充个最终操作:
public class Program {
public static void main(String[] args) {
// ... ...
//最终操作7:findFirst 和 findAny
//findFirst:获取流中的第一个元素,无论是并行流还是串行流,取的都是流中的第一个元素
System.out.println(Data.getData().parallelStream().findFirst());
System.out.println(Data.getData().stream().findFirst());//使用串行流效果是一摸一样的
//findAny:获取流中的任意一个元素,对于串行流结果等同于findFirst,并行流中则不是
//在并行流的环境下,使用findAny比findFirst效率要高
System.out.println(Data.getData().stream().findAny());
System.out.println(Data.getData().parallelStream().findAny());
}
}
五、Collectors工具类
Collectors是一个工具类,提供了若干个方法,返回一个Collectors接口的实现类对象。在最终操作中有简单的使用了Collectors的toList()、toSet()、toMap(),我们再来看下其他常用的几个方法。
import java.util.IntSummaryStatistics;
import java.util.stream.Collectors;
public class CollectorsDemo {
public static void main(String[] args) {
//maxBy 和 minBy
//maxBy:通过指定的规则,获取流中最大的元素
//minBy:通过指定的规则,获取流中最小的元素
System.out.println(Data.getData().stream().collect(Collectors.maxBy((ele1, ele2) -> ele1.getScore() - ele2.getScore())));
System.out.println(Data.getData().stream().collect(Collectors.minBy((ele1, ele2) -> ele1.getAge() - ele2.getAge())));
//joining:合并,将流中的元素,以字符串的形式拼接起来
//需求:将集合中所有的Person对象的姓名拼接成一个字符串
//无参,没有分隔符直接拼接
String ret1 = Data.getData().stream().map(Person::getName).collect(Collectors.joining());
System.out.println(ret1);
//一个参,分隔符(逗号拼接)
String ret2 = Data.getData().stream().map(Person::getName).collect(Collectors.joining(","));
System.out.println(ret2);
//三个参,分别为拼接符、前缀、后缀
String ret3 = Data.getData().stream().map(Person::getName).collect(Collectors.joining(",", "[", "]"));
System.out.println(ret3);
//summingInt,summingDouble,summingLong
//summingInt:计算int类型的和,将流中的每一个元素映射成int类型然后求和
//需求:将集合中的所有人的成绩求和
System.out.println(Data.getData().stream().collect(Collectors.summingInt(Person::getScore)));
//averagingInt,averagingDouble,averagingLong
//averagingInt:计算int类型的平均值,将流中的每一个元素映射成int类型然后求平均值
System.out.println(Data.getData().stream().collect(Collectors.averagingInt(Person::getScore)));
//summarizingInt,summarizingDouble,summarizingLong
//summarizingInt:将流中的每一个元素映射成int类型然后获取这些元素的描述信息,对这些int类型进行了简单描述
//输出结果为:IntSummaryStatistics{count=7, sum=555, min=60, average=79.285714, max=100}
System.out.println(Data.getData().stream().collect(Collectors.summarizingInt(Person::getScore)));
//返回类型为IntSummaryStatistics
IntSummaryStatistics ret = Data.getData().stream().collect(Collectors.summarizingInt(Person::getScore));
System.out.println(ret.getCount());//个数
System.out.println(ret.getMax());//最大值
System.out.println(ret.getMin());//最小值
System.out.println(ret.getSum());//和
System.out.println(ret.getAverage());//平均值
}
}
over ~