java基础之Stream流(一)

流提供一种比集合更高级指定计算的视图。通过使用流,我们可以说明想要完成什么任务,而不是说明如何去实现它。我们将操作的调度留给具体的实现去解决。例如,假设我们想要计算某个属性的平均值,那么我们可以指定数据源和该属性,然后,流库可以对计算进行优化。例如,实用多线程来计算总和与个数,并将其合并。

1.1 从迭代到流的操作

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

String content = new String(Files.getAllBytes(Paths.get("1.txt"))
                ,StandardCharsets.UTF-8);
List<String> words = Arrays.aList(content.split("//PL+"));

//用普通的方法迭代
long count=0;
for(String word : words){
    if(word.length()>12){
        count++;
    }
}
//使用流后

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

可以看出流的版本你循环更易于阅读,因此我们没必要扫描整个代码去查找过滤和计数操作,方法名就可以直接告述我们其代码意欲何为。而且,循环需要非常详细的指定操作的顺序,而流却能将以想要的任何方式来调度这些操作,只要结果正确即可。

1.2 流的特点

  1. 流并不储存其元素。这些元素可能储存在底层的集合中,或者按需生成。
  2. 流的操作不会修改数据源。例如,filter方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤的元素。
  3. 流的操作是尽可能惰性执行的。这意味着直到需要其结果时,操作才会被执行。例如,我们只想要查找前5个长单词而不是所有长单词,那么filter方法就会匹配到第5个单词后就会停止过滤。因此,我们可以操作无限流。

1.3 流的创建

        如果有一个数组可以使用静态的Stream.of方法来转换流。

Stream<String> words = Stream.of(content.split("//PL+"));

of方法具有可变长参数,因此我们可以构建具有任意数量引元的流:

Stream<String> songs = Stream.of("gently","down","the","stram");

使用Arrays.stream(array,from,to)可以从数组中位于from(包括)到to(不包括)的元素创建一个流。为了创建不包含任意元素的流可以使用可以使用静态的Stream.empty方法:

Stream<String> empty = Stream.empty();

Stream接口有两个用于创建无限流的静态方法。generate方法会接受一个不包含任意引元的函数(从技术上讲,是一个Supplier<T>接口的对象)。无论何时,只需要一个流类型的值,该函数就会调用以产生一个这样的值。我们可像下面这样获取一个常量值的流:

Stream<String> echos = Stream.generate(() -> "Echo");

或者像下面获取一个随机数流

Stream<Integer> random = Stream.generate(Math::random);

为了产生无限序列,例如0,1,2,3....,可以使用iterate方法,它会接受一个"种子"值,以及一个函数(从技术上讲,是一个UnaryOperation<T>接口) ,并会反复的将该函数应用到之前的结果上。

Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO,
                                            n -> add(BigInteger,ONE));

该元素的第一个种子BigInteger.ZERO,第二个f(speed),即1(作为大整数),下一个元素是f(f(speed)),即2,以此类推。

1.4 filter,map和flatMap方法

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

List<String> wordList = new ArrayList<>();
wordList.add("teacher");
wordList.add("student1");
wordList.add("teacher1");
wordList.add("student");

Stream<String> stream = wordList.stream().filter(w -> w.length()>7);

filter的引元是Predicate<T>,即T到boolean类型的函数。

通常我们想要以某种方式转换流中的值,此时,可以使用map方法并传递转换流中元素的函数。例如,下面将所有单词转换小写:

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

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

Public static Stream<String> letters(String s){
    List<String> list = new ArrayList<>();

    for(int i=0; i<s.lenght(); i++){
        list.add(s.substring(i,i+1));
    }

    return list.stream();
}

假设我们在一个字符串的流上映射letters方法:

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

那么就会得到类似于二维数组包含流的流。为了将其摊平成一维数组,要使用flatMap而不是map:

Stream<Stream<String>> stream = word.stream().flatMap(w -> letters(w)); 

1.5 抽取子流和连接流

        调用stream.limit(n)会返回一个新的流,它会在n个元素之后结束(如果原来的流更短,那么就会在原来的流结束时结束)。这个方法对于裁剪无限流的尺寸显的特别有用。例如,

Stream<Double> random = Stream.generate(Math::random).limit(100);

        掉用stream.skip(n)正好相反:它会丢弃前n个元素。这个方法将文本分隔成单词时,显的特别方便,因为按照split的工作方式,第一个元素是没什么用的空字符串。可以通过skip跳过它:

Stream<String> words = Stream.of(content.split("//PL+")).skip(1);

 

        也可以使用Stream的静态方法cancat把两个流连接起来:

Stream<String> word = Stream.concat(letter("teacher"),letter("student"));

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值