【core Java学习笔记】Java SE8 流库 Stream Library
从迭代到流
如果要计算一个文本中有多少长单词(字母>12)。
迭代式:
words = getlist();//虚构方法,获得一个List<String> long count = 0; for(String w:words) { if(w.length()>12) count++; }
流式:
words = getlist();//虚构方法,获得一个List<String> long count = words.stream() .filter(w->w.length() > 12) .count();
- 流式表达式相对于迭代式的好处:
- 易于阅读。
- 易于优化,例如将
steam()
方法换为parallelStream()
方法就可以进行并行计算。 - 遵循"what, not how"原则,抽象层次更高。
- 流的特征:
- 流不存储元素,元素可能存储在按需生成的匿名集合中。
- 流操作绝不会改变源数据。例如
filter()
方法不会移除数据,而是产生新的流。 - 流操作是lazy的,意味着直到流操作的结果被需要时才会进行运算。
- 流操作的范式:
- 建立流。例如上例的
stream()
- 应用
intermediate operations
使用原流去产生其他需要的流,可以进行多步。例如上例的filter()
- 应用
terminal operation
去产生结果。之后流将不可用。例如count()
- 建立流。例如上例的
建立流
- 如果有
Collection
类,调用其stream()
或其他流方法。 - 如果有数组,使用
Stream.of(Array array)
方法。 - 创建空流,调用
Stream.empty()
方法。 创建无限长流,调用
Stream.generate(Supplier supplier<T> s)
方法。Stream<String> echos = Stream.generate(()->"Echo"); //上方lambda表达式可写为如下完整表达式 Stream<String> echos = Stream.generate(new Supplier<String>() { @Override public String get() { return "echo"; } });
Stream<Double> randoms = Stream.generate(Math::random); //产生随机数,双冒号是lambda表达式中的方法引用
创建迭代的无限长流,使用
Stream.iterate(T seed,UnaryOperator f)
Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO,n->n.addd(BigInteger.ONE)) //流的第一个元素为seed,后续元素为f(seed),f(f(seed))...
字符串创建流,调用
Pattern
类中的spliteAsStream(CharSequence input)
方法Pattern pattern = Pattern.compile(" "); Stream<String> s = pattern.splitAsStream("A B C");
filter
,map
和flatmap
方法
最常用的三种intermediate operations
Stream<T> filter
方法:选择源流中符合一定条件的元素组成新流。List<String> wordList = ...; //省略初始化 Stream<String> longWords = wordList.Stream().filter(w -> w.length() > 12);
<R> Stream<R> map
方法:对源流中所有元素执行同一操作后组成新流。List<String> words = ...; //省略初始化 Stream<String> lowercaseWords = words.stream().map(String::toLowerCase);
<R> Stream<R> flatmap
方法:和map
方法的效果大致相同,但对多个流组成的流操作会得到合并的一个流。即对Stream提取子流以及合并流
调用
Stream<T> limit(n)
来提取前n个元素的子流。Stream<Double> randoms = Stream.generate(Math::random).limit(100);
调用
Stream<T> skip(n)
来获得跳过前n个元素的子流Stream<String> words = Stream.of(contents.split("\\PL+")).skip(1);
调用
Stream<T> concate(Stream a,Stream b)
来创建a,b流合并后的流,b在a后。其他流转换操作
Stream<T> distinct()
获得相同顺序,但是只保留一个相同项的流。Stream<String> uniqueWords = Stream.of("a","a","b","c","a").distinct();
使用
Stream<T> sorted()
对comparable排序,或使用Stream<T> sorted(Comparator<? super T> comparator)
对普通对象元素排序。Stream<String> longestFirst = words .stream() .sorted(Comparator.comparing(String::length)) .reversed();
使用
Stream<T> peek(Consumer<? super T> action)
在每次元素实际被访问时触发动作(action),利于debug。Object[] powers = Stream.iterate(1.0,p -> p * 2) .peek(e - > System.out.println("Peeking"+e)) .limit(20).toArrays();
简单的还原(reduction)操作
- 还原操作是将流对象转换为非流对象的操作。
- 还原操作是终止操作(terminal operations),操作之后流不可用。
count()
计算流中元素个数。(上例有)max(Comparator<? super T> comparator)
和min(Comparator<? super T> comparator)
获得最大/最小元素。他们返回一个Optional<? super T>
类。Optional<String> largest = words.max(String::compareToIgnoreCase); System.out.println("largest:" + largest.orElse(""));
findFirst()
和findAny()
获得过滤器过滤到的第一个/所有元素。他们返回一个Optional<? super T>
类。Optional<String> startsWithQ = words.filter(s -> s.startWith("Q")).findFirst();
anyMatch(Predicate<? super T> p)
,allMatch(Predicate<? super T> p)
,noneMatch(Predicate<? super T> p)
返回匹配结果(boolean)。boolean aWordStartsWithQ = words.parallel().anyMatch(s -> s.startWith("Q"));
Optional
类
Optional
类是流还原操作的常用返回值。Optional<T>
持有(wrap)T类型对象或null的对象。这样的设计思路是为了安全,避免空引用。
简单地处理Optional
对象中持有的对象。
T orElse(T other)
如果该Optional
对象非空,就返回持有的对象,否则返回other
。String result = optionalString.orElse(""); //return wrapped string, or "" if null
T orElseGet(Supplier<? extends T> other)
如果该Optional
对象非空,就返回持有对象,否则返回other.get()
T orElseThrow(Supplier<? extends X> exceptionSupplier)
,如果该Optional
对象非空,就返回持有的对象,否则抛出一个exceptionSupplier.get()
异常。ifPresent
接受一个函数,如果option持有对象,则用该函数处理;否则无事发生。
optionValue.ifPresent(v -> Process v);
。该方法无返回值。map
方法和ifPresent
类似,但是有一个Optional<T>
的返回值,原Optional<T>
对象不变。
正确地使用Optional
类
随时警惕
Optional
对象可能不持有对象。这种特性可能造成NoSuchElementException
错误的抛出。Optional<T> optionValue = ...; optionValue.get().someMethod(); //not safe, cause get() may produce null
Optional<T> optionValue = ...; if(optionValue.isPresent()) optionValue.get().someMethod(); //safe, cause isPresent() reports whether an Optional object has a value
创建
Optional
类的对象创建
Optional
对象的主要方法是调用Optional
类的静态工厂方法。static <T> Optional<T> of(T value) 产生一个Optional对象,如果value是null,则抛出异常 static <T> Optional<T> ofNullable(T value) 产生一个Optional对象。 static<T> Optional
通过
flatMap
方法调用Optional<T>
对象持有的T对象的方法。
//f() yields an Optional<T>
//T has a method g() yields an Optional<U>
Optional<U> result = s.f().flatMap(T::g)
收集流到集合中
展示流
stream.forEach(System.out::println);
获得数组
String[] result = stream.toArray(String::new); //toArray get Object[], pass it to constructor to get the right type.
使用
collect
方法和Collectors
类中众多的工厂方法来获得不同容器中的对象集合。
List<String> result = stream.collect(Collectors.toList());
Set<String> result = stream.collect(Collectors.toSet());
TreeSet<String> result = stream.collect(Collectors.toCollection(TreeSet::new));
//control which type of implementation of Collection
String result = stream.collect(Collectors.joining());
String result = stream.collect(Collectors.joining(", "));
//joing to one String
Iterator<T> iterator = stream.iterator();
IntSummaryStatistics summary = stream.collect(
Collectors.summarizingInt(String::length));
double averageWordLength = summary.getAverage();
double maxWordLength = summary.getMax();
//summarizing(Int|Long|double) -> (Int|Long|Double)SummaryStatistisc
收集流到字典中
例如有一个Person类
Person{
private int id;
private String name;
public getId(){
return id;
}
public getName(){
return name;
}
public Person(int id,String name){
this.id = id;
this.name = name;
}
}
- 一般情况
Map<Integer, String> keyToValue = stream.collect(
Collectors.toMap(Person::getID,Person::getName));
//(Function keyMapper, Function valueMapper)
- 将对象整个作为键/值
Map<Integer, Person> idToPerson = stream.collect(
Collectors.toMap(Person::getID,Function.identity()));