1 其他流的转换
distinct方法会返回一个流,它的流是从原有的流中产生的,即原来的元素按照同样的顺序剔除重复元素后产生的。这个流显然能记住它已经见过的元素。
Stream<String> stream = Stream.of("work","word","sister","word")
.distinct();
对于流的排序,有多种sorted方法变体可用。其中一种利用操做Comparable元素的流,而另一种可以接受一个Comparator。下面是对一个字符串进行排序,是最长的字符串排到最前面:
List<String> word = new ArrayList<>();
word.add("word");
word.add("student");
Stream<String> longestFirst = word.stream()
.sorted(Comparator(String::length).reversed());
与所有的转换流一样,会产生一个新的流,它的元素还是原有流按照顺序排列的元素。
当然,我们对集合的排序可以不使用流。但是,当排序处理成为流管道的一部分时,sorted方法就显得有用了。
最后peek会产生一个新的流,它的元素与原来的元素相同,但每次获取一个元素时都会调用一个函数。这对于调试非常方便。
Object[] power = Stream.iterate(2.0,p -> p*2)
.peek(e -> System.out.println("Fething"+e))
.limit(20)
.toArray();
当实际访问一个元素时,就会打印一条消息。通过这种方式你就可以验证iterate返回的无限流是被惰性处理的。对于调试,你也可以让peek调用一个你设置断点的地方。
2 简单约简
约简是一种终结操作,它们会将流约简成可以在程序中使用的非流值。
count方法就是一种简单约简,会返回流中元素的数量。其他的简单约简还有max和min,它们会返回最大值和最小值。这两个方法返回的是Optional<T>的值,它要么在其中包装的答案,要么表示没有任何值。在过去,碰见这种情况返回null很常见,但这样做会导致在未做完备测试的程序抛出空指针异常。Optional是一种表示缺少返回值的一种更好的方式。
findFirst是查找非空集合中的第一个元素。它通常与filter配合使用才显得有用。例如,下面展示了如何找到第一个以Q开头的单词。前提是存在这样的单词:
Optional<String> startWithQ = words.filter(s -> s.startsWith("Q")).findFirst();
如果不强调第一个匹配,而是使用任意匹配,那么使用findAny方法。这个方法在并行处理流方面很有效。
Optional<String> startWithQ = words.parallel()
.filter(s -> s.startsWith("Q")).findFirst();
如果只想知道,是否存在匹配,可以使用anyMatch.这个方法会接受一个断言引元,因此不需要使用filter。
Optional<String> startWithQ = words.parallel()
.anyMatch(s -> s.startsWith("Q"));
还有allMatch和noneMatch方法,他们分别在所有元素和没有任何元素匹配的情况下返回true。这些方法都可以在并行情况下获益。
3 Optional类型
Optional<T>对象是一种包装器对象,要么包装了类型T的对象,要么没有包装任何对象。对于第一种情况,我们称为这种值是存在的。Optional<T>类型被当作一种更安全的方式,用来代替类型T的引用,这种引用要么引用某个对象,要么为null。但是,他只有正确使用的情况下才会更安全。
3.1 如何使用Optional值
有效地使用optional的关键是要使用这样的方法:它的值不存在的情况下会产生一个可替代物,而只有在值存在的情况下才会使用这个值。
通常,在没有任何匹配时,我们会希望使用某种默认值,可能是空字符串:
String result = OptionalString.orElse("");
可以调用代码来计算默认值
String result = optionalString.orElseGet(
() -> Locale.getDefault().displayName());
或者在没有任何值时抛出异常:
String result = optionString.orElseThrow(IllegalStateException::new);
上面是当optional中,不存在任何值时,就会产生一个替代物。另一条使用可选值的策略是只有其值存在的情况下才消费改值。
ifPresent()方法会接受一个函数。如果该可选值存在,那么它会被传递给该函数。否则,不会发生任何事情。
optionalValue.ifPresent(v -> process V)
例如,如果该值存在的情况下,想要将其添加到某个集中,那么就可以调用
optionalValue.ifPresent(v -> results.add(v));
也可以这样调用:
optionalValue.ifPresent(results::add);
当调用ifPresent时,从该函数不会返回任何值。如果想要处理函数的结果,应该使用map:
Optional<Boolean> added = optionalValue.map(results::add);
现在added具有三种值之一:在optionalValue存在的情况下包装在Option中的true或false,以及optionvalue不存在的情况下的空Optional。
常用方法
- T orElse(T other) 产生这个option的值,或者在optional为空的情况下,产生other。
- T orElseGet(Suplier<? extends T > other) 产生这个Optional的值,或者在optional为空的情况下,产生other的结果。
- <x extends Throwable>T orElseThrow(Suplier<? extends T > exceptionSuplier) 产生这个Optional的值,或者在optional为空的情况下,抛出调用exceptionSuplier的结果。
- void ifPresent(Consumer<? super T> consumer) 如果optional的值不为空,就把值传递给consumer。
- <U> Optional<U> map(Function<? super T, ? extends U> mapper) 产生将该Optional的值传递给mapper后的结果,只要这个optional不为空且结果不为null,否则会产生一个空的Optional对象。
3.2 不适合使用Optional值的方式
如果没有正确的使用Optional值,那么相较以往的得到"某物或null"的方式,你并没有得到任何好处。
get方法会在Optional值存在的情况下获得其中包装的元素,或在不存在的情况下抛出一个NoSuchElementException对象。因此,
Optional<T> optionalValue = ...;
optionalvalue.get().someMethod();
上面方法并不比下面的安全
T value = ....;
value.someMethod();
isPresent()方法会报告某个Optional<T> 对象是否具有一个值。但是
if(optionalValue.isPresent()){
optionalValue.get().someMethod();
}
//并不比下面的方式更容易处理
if(value != null){
value.someMethod();
}
常用方法
- T get() 产生这个Optional的值,或者在该Optional为空时,抛出一个NoSuchElementException
- boolean isPresent() 如果该Optional不为空,则返回true。
3.3 创建Optional值
如果想要编写方法来创建Optional对象,那么有多个方法可以用于创建
public static Optional<Double> inverse(){
return x == 0 ? Optional.empty() : Optional.of(1/x);
}