1. Stream只能被使用一次
- 转换操作:把Stream转换成另一个Stream(例如map方法),该Stream会被标注为operated。
- 终止操作:对stream应用终止操作(例如count方法),改Stream会被标注为closed。
不管是应用转换操作还是终止操作,改Stream都不能再用了,再用就会抛出运行时异常:java.lang.IllegalStateException: stream has already been operated upon or closed。
Stream<String> words = Stream.of("a", "aa", "aaa", "aaaa"); Stream<String> result = words.filter(w -> w.length() > 2); long count = words.count(); // Throw exception as stream is already been used.
反过来也一样,如果先使用了count方法,再调用filter方法,也会抛出java.lang.IllegalStateException。
一次性的卫生筷子,用完后还可以将就再用用,但Stream用完就不能再用了。故个人认为不适合作为类的成员变量,适合作为方法的参数,返回值或者局部变量。
class Depart { // Once stream is used, it will exists like a garbage and cannot be collect by GC. private Stream<Integer> stream = Stream.of(1, 2, 3); private Stream<String> doSomething(Stream<Integer> input) { Stream<Integer> distintInput = input.distinct(); return distintInput.map(n -> String.valueOf(n)); } }
可以调用close方法,主动终结一个Stream。
Stream<Integer> stream = ... stream.close();
2. Stream – flatMap()方法
// Get team members of given team name. // retrieveTeamMembers(String team): Stream<String> Stream<String> teams = Stream.of("GUI", "Server", "SQA", "PEG"); Stream<Stream<String>> result = teams.map(team -> retrieveTeamMembers(team));
result是个Stream,其数据类型是Stream<String>:[[“Alex”, “Jason”, …], [“Luc”, “Emma”, …], […]]。
使用flatMap,可以将子Stream展开成一个Stream:[“Alex”, “Jason”, …, “Luc”, “Emma”, …]。
Stream<String> result = teams.flatMap(team -> retrieveTeamMembers(team)); // Then we can apply action on each element. result.forEach(System.out::println);
使用场合:当map操作返回的是Stream<Stream>,而又需要对结果中每个Stream中的元素进行相同的处理,应当使用flatMap将所有的Stream展开成一个Stream。
3. Stream – peek()方法
peek方法会产生一个与原Stram具有相同元素的Stream,但是每次获取一个元素时,都会调用一个函数。
Optional<String> maxString = Stream.of("a", "aaaa", "aa", "aaaaaa") .peek(s -> s = s + "!") .map(s -> s.toUpperCase()) .peek(s -> Logger.logMsg(Logger.INFO, s)) .max(Comparator.comparing(String::length));
上面代码中的两个peek方法存在与否,对结果没有任何影响。利用peek的这个特性,我么可以在添加测试代码,如上面的Logger.logMsg()方法。
4. Stream – 元素排序
Collections.sort()方法会对原来的集合进行排序,而Stream的排序是生成一个新的排序的Stream。
去除重复元素 – distinct
对于基本数据类型,使用相等(==)来判断元素是否重复。
Stream<String> days = Stream.of("Monday", "Monday", "Tuesday" ); long count = days.distinct().count();
对于对象类型,根据是否引用同一个对象,来判断是否重复。想要去除equals相等的重复对象,distinct()做不到。
排序 – sorted
Stream中的元素,实现了Comparable接口,直接使用sorted()方法。
class Session implements Comparable<Session> { private int id = 0; // Constructor, setter and getter } Stream<Session> sessions = Stream.of(new Session(88), new Session(45), new Session(55), new Session(23)); Stream<Session> sortedSessions = sessions.sorted();
Stream中的元素,没有实现Comparable接口,需要传入一个Comparator对象,调用Comparator的reversed方法就是逆序排序。最简单的,就是使用Stream中元素某个方法的返回值,进行排序。
// Sort session according to it's id. Stream<Session> sortedSessions = sessions.sorted(Comparator.comparing(Session::getId)); Stream<String> words = ... words.sorted(Comparator.comparing(String::length)); words.sorted(Comparator.comparing(String::length).reversed());
某些情况下,无法从直接从Stream的Element中获取排序关键字。需要先构造排序的key,然后指定根据key的排序规则。看下面的Scenario:
有一个Student的Stream,使用student的ID可以获得Student的所有成绩Grade对象,Grade对象包含学生所有的成绩。根据学生的数学成绩排序。
// GradeUtil::getGrade(int), get a student's Grade according it's id. // Grade::mathGrade(), get a student's math grade. Stream<Student> students = ... students.sorted(Comparator.comparing( student -> { return GradeUtil.getGrade(student.getId()); }, Comparator.comparing(Grade::mathGrade)));
Comparator里面还有很多好用的方法,如nullsFirst(),把null元素排在前面;nullsLast(),把null元素排在后面。
5. Stream – 元素检索
从Stream中检索出来的元素,都会封装在Optional<T>里,类似ObjectHolder<T>。调用get()方法返回封装数据,如果没有,则抛出NoSuchElementException。
方法名 |
用途 |
findFirst(): Optional<T> |
返回集合的第一个元素 |
findAny(): Optional<T> |
返回集合中的任何一个元素 |
anyMath(Predicate): boolean |
有一个元素匹配,就返回True |
allMatch(Predicate): boolean |
全部匹配时,返回True |
noneMatch(Predicate): boolean |
都不匹配时,返回True |
findFirst()和findAny()通常与filter()结合使用,先过滤再查找。元素检索,为了提高效率,通常采用并行Stream。
List<String> words = ... Optional<String> result1 = words.parallelStream().filter(s -> s.startsWith("Q")).parallel().findFirst(); Optional<String> result2 = words.parallelStream().filter(s -> s.endsWith(".txt")).parallel().findAny(); boolean anyMatch = words.parallelStream().anyMatch(s -> s.startsWith(".")); boolean allMatch = words.parallelStream().allMatch(s -> s.startsWith(".")); boolean noneMatch = words.parallelStream().noneMatch(s -> s.startsWith("."));
max()和min() 方法,需要传入一个Comparator,检索出最大元素和最小元素。
class Book { private long id; private int pages; private double price; // Getter and setter } List<Book> books = ... Optional<Book> thickBook = books.parallelStream().max(Comparator.comparing(Book::getPages)); Optional<Book> cheapBook = books.parallelStream().min(Comparator.comparing(Book::getPrice));
6. Stream.empty()
生成一个空的Stream,多用于方法的返回值,避免返回null。