java SE 8的流库

  流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图。通过使用流,我们可以说明想要完成什么任务,而不是说明如何去实现它。我们将操作的调度留给具体实现去解决。例如,假设我们想要计算某个属性的平均值,那么我们就可以指定数据源和该属性,然后,流库就可以对计算进行优化,例如,使用多线程来计算总和与个数,并将结果合并。java SE 8引入的流库是用来以“做什么而非怎么做”的方式处理集合。

1、从迭代到流的操作

  在处理集合时,我们通常会迭代遍历它的元素,并在每个元素上执行某项操作。例如,假设我们想要对某集合(链表)中所有的长单词进行计数,我们对集合中的元素进行迭代:

List<String> words = ...
long count = 0;
for(String w : words)
{
   if(w.length() > 13) count++;
}

  在使用流时,相同的功能操作代码如下:

long count = words.stream().filter(w -> w.length() > 13).count();

  流的代码版本比循环版本要以与阅读,而且更加简单,我们用流来实现时我们提供方法名就可以直接告诉我们其代码意欲合为,而且循环实现要非常详细地指定操作的顺序,而流却能够以其想要的任何方式来调度这些操作,只要结果正确就可以。

  仅将stream修改为parallelStream就可以让流库以并行方式来执行过滤和计数。

long count = words.parallelStream().filter(w -> w.length() > 13).count();

  流遵循了“做什么而非怎么做”的原则。

  流表面上看起来和集合很类似,都可以让我们转换和获取数据,但是它们之间存在着显著的差异:

1.流并不存储其元素。这些元素可能存储在底层的集合中,或者是按需生成的。

2.流的操作不会修改其数据源。例如filter方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素。

3.流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。例如如果我们只想要查找前10个长单词而不是所有长单词,那么filter方法就会匹配到第10个单词后停止过滤。因此我们甚至可以操作无限流。

  我们的这个计算长单词的示例的工作流时操作流时的典型流程。我们建立了一个包含三个阶段的操作管道:

1.创建一个流。

2.指定将初始流转换为其他流的中间操作,可能包含多个步骤。

3.应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作。从此之后,这个流就再也不能用了。

//java.util.stream.Stream<T>
Stream<T> filter(Predicate<? super T> p)
//产生一个流,其中包括当前流中满足p的所有元素。

//java.util.Collection<E>
default Stream<E> stream()
default Stream<E> parallelStream()
//产生当前集合中所有元素的顺序流或并行流。

2、流的创建

//java.util.stream.Stream 8

static <T> Stream<T> of(T...values)            //产生一个元素为给定值的流
static <T> Stream<T> empty()                   //产生一个不包括任何元素的流
static <T> Stream<T> generate(Supplier<T> s)   //产生一个无限流,它的值是通过反复调用函数s而构建的
static <T> Stream<T> iterate(T seed,UnaryOperator<T> f)
//产生一个无限流,它的元素包括种子、在种子上调用f产生的值、在前一个元素上调用f产生的值,等等


//java.util.Arrays 1.2
static <T> Stream<T> stream(T[] array, int startInclusive, int endExlusive)
//产生一个流,它的元素是由数组中指定范围的元素构成的

//java.util.regex.Pattern 1.4
Stream<String> splitAsStream(charSequeue input)
//产生一个流,它的元素是输入中由该模式界定的部分

//java.nio.file.Files 7
static Stream<String> lines(Path path)
static Stream<String> lines(Path path,Charset cs)
//产生一个流,它的元素是指定文件中的行,该文件的字符集为UTF-8,或者为指定的字符集。

//java.util.function.Supplier<T> 8
T get()              //提供一个值

 

3、filter、map和flatMap方法

  流的转换会产生一个新的流,它的元素派生自另一个流中的元素。我们已经看到了filter转换会产生一个流,前面示例将一个字符串流转换为了只包含长单词的另一个流,filter转换后产生的流的元素与某种条件相匹配。

  通常我们想要按照某种方式来转换流中的值,此时可以使用map方法并传递执行该转换的函数。例如,我们可以像下面这样将所有单词都转换为小写:

Stream<String> lowercaseWords = words.stream().map(String::toLowerCase);

  这里我们使用的是带有方法引用的map,但是通常我们可以使用lambda表达式来代替:

Stream<String> firstLetters = words.stream().map(s -> s.substring(0,1));

  上面的语句所产生的流中包含了所有单词的首字母。

  在使用map时,会有一个函数应用到每个元素上,并且其结果是包含了应用该函数后所产生的所有结果的流。现在假设我们有一个函数,它返回的不是一个值,而是一个包含众多值的流:

public static Stream<String> letters(String s)
{
  List<String> result = new ArrayList<>();
  for(int i = 0; i<s.length(); i++)
    result.add(s.substring(i,i+1));
  return result.stream();
}

  例如letters("hello")的返回值是流["h", "e", "l", "l", "o"]。

  我们在一个字符串流["you", "are", "welcome"]上映射letters方法:

Stream<Stream<String>> result = words.stream().map(w -> letters(w));

  那么就会得到一个包含流的流[["y", "o", "u"], ["a", "r", "e"], ["w", "e", "l", "c", "o", "m", "e"]]。为了将其摊平为字母流["y", "o", "u", "a", "r", "e", "w", "e", "l", "c", "o", "m", "e"],可以使用flatMap方法而不是map方法:

Stream<String> flatResult = words.stream().flatMap(w -> letters(w));
//calls letters on each word and flattens the results
//java.util.stream.Stream 8
Stream<T> filter(Predicate<? super T> predicate)
//产生一个流,它包含当前流中所有满足断言条件的元素
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
//产生一个流,它包含将mapper应用到当前流中所有元素所产生的结果
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
//产生一个流,它是通过将mapper应用与当前流中所有元素所产生的结果连接在一个而获得的

  注意:这里的每一个结果都是一个流。

 

5、简单约简

  已经讨论了如何创建流和转换流,现在可以讨论最重要的部分了,即从流数据中获得答案,可以称之为约简。约简是一种终结操作,它们会将流约简为可以在程序中使用的非流值。

  之前示例中的一种简单约简:count方法会返回流中元素的数量。其他部分约简如下:

Optional<T> max(comparator<? super T> comparator)
Optional<T> min(comparator<? super T> comparator)
//分别产生这个流的最大元素和最小元素,使用由给定比较器定义的排序规则,如果这个流为空,会产生一个空的//Optional对象
Optional<T> findFirst()
Optional<T> findAny()
//分别产生这个流的第一个和任意一个元素,如果这个流为空,会产生一个空的Optional对象
boolean anyMatch(Predicate<? super T> predicate)
boolean allMatch(Predicate<? super T> predicate)
boolean noneMatch(Predicate<? super T> predicate)
//分别在这个流中任意元素、所有元素和没有任何元素匹配给定断言时返回true。
//这些操作都是终结操作

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yisun03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值