- Java高级特性第一章(Java8的流库)
- 前言
- 1.1 流迭代
- 流迭代总结
- 1.2 流的创建
- 只传两个参数时
- 多传一位参数时
- 注意
- 重点
- 1.3 filter.map和flatMap方法
- 假设
- ★★ 在流之外也会发现有`flatMap`方法因为它是计算机科学中的一种通用的概念.
- 补充:
- 1.4 抽取子流和组合流
- 抽取子流和组合流总结
- 结尾
提示:本文是OGtwelve学习高级特性时所总结内容;
1.1 流迭代
流迭代
Long count = words.stream().filter(w -> w.length()>12).count(); //使用名为count的Long类型接收; 先获取words字符串集的流,并通过filter进行过滤(判断长度大于12的字符串)并最后进行个数统计
并行流
Long count = words.parallelStream().filter(w -> w.length()>12).count()
在上方示例中,filter会根据条件返回一条新流,count的作用是将流的个数化简为一个结果,stream和parallerStream为创建,filter为转换,count为终止
流迭代总结
流的操作不会修改其数据源
例如: filter则生成新的流,不包括被滤掉的元素
(filter生成过滤后的新流并不包含过滤掉的元素)
(流的操作是惰性执行的,也就意味着找到流中最后一个元素才会执行,因此甚至可以操作无限流)
- 可以用Collection接口的Stream方法将任何集合转换为一个流 , 如有数据 , 可用
Streamwords = Stream.of(contents.split("//PL+"));
of方法具有可变长参数 , 因此可以构建具有任意数量引元的流.
[Stream接口有两个用于创建无限流的静态方法generate();方法会接收一个不包含任何引元的函数(或者从技术上讲,是一个Supplier接口对象)] - 使用Array.stream(array,from,to);可以用数组中的一部分元素来创建一个流.
- 创建不包含任何元素的流,可以用Stream.empty();方法.
常量值的流:
Streamechos = stream.generate(() -> "Echo");
随机数的流:
Streamrandoms = stream.generate(Math::random);
※※ 要产生(0,1,2,3…N)这样的序列,可以使用iterate方法,它会接收一个种子值,以及一个函数(准确的说是一个UnaryOpreation
例如:
Streamintegers = Stream.iterate(BigInteger.Zero,n -> n.add(BigInteger.One)) ; //在该序列中,第一个元素是BigInteger.ZERO,第二个元素是f(seed),即是1(大整数BigInteger), //下一个元素为f(f(seed),即是2;后续以此类推
※※ 如果要产生一个有限序列,则需在参数后添加一个谓词 (参数) 来描述迭代应如何结束
例如:
var limit = new Integer("10000000");
Stream integers = Stream.iterate(BigInteger.Zero , n -> compareTo(limit) < 0, n -> n.add(BigInteger.One))
(只要该谓词拒绝了某个迭代生成的值 , 这个流即结束)
只传两个参数时:(第一个值为类型,第二个值为UnaryOpreation
:(再多传一位时,第二个值为(lambda)表达式,之前第二个值的位置即被推到第三个值的位置.)
Stream.ofNullable方法会用一个对象来创建一个非常短的流.
如果该值判断为null,则这个流的长度为0(仅长度为0,依然存在一条流),当不为null时,长度则为1,内容即为当前判断的该对象.(与flatMap结合最适用)
Java的API接口中有大量方法可以产生流 ;
示例1
例如Pattern类下有一个splitAsStream方法,会按照某个正则表达式来分割一个charSequence对象.
Pattern.compile("//PL+").splitAsStream(contents);
//contents为要分割的charSequence对象
示例2
Scanner.tokens方法会产生一个扫描器的符号流,另一种从字符串获取单词流.
Streamwords = new Scanner(contents).tokens(); //contents依旧为需要获取tokens的内容
示例3
静态的File.lines方法会返回一个包含了文件中所有行的Stream;
try(Streamlines = File.lines(path)){ Process lines.//处理获取到的流 } //path在这里是文件的路径
示例4
如果持有的Iterable对象不是集合,那么可以通过下方转换为一个流
StreamSupport.stream(iterable.spliterator(),false);
如果持有的Iterable对象是集合并希望得到一个由它的结果构成的流,那么例:
StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator,spliterator.ORDERED),false); //转换流的时候,将未知大小的分辨卸载第一个值的位置,boolean类型的判断依旧存在于句尾.
重点
- 在执行流的操作的时候,并没有修改流背后的集合.
- 流并没有收集其数据,数据一直存储在单独的集合中.
- 如果修改了该集合,那么流操作的结果就会变成未定义的
(JDK文档称这种要求为不干涉性)
准确的说,因为中间流的操作是惰性的,即在终止处执行,集合有可能已经发生了变化。
(执行后原流的值被更新,差不多是一个即时时效性的问题,一般线程同步sync或平行parallel来解决 --> 括号内部分方便理解自己定义的)
流的转换会产生一个新的流,它的元素会派生自另一个流中的元素,我们已经看到了filter转换会产生一个新流,它的元素与某种条件相匹配.
1.这部分将一个字符串专户那位只包含长单词的另一个流.
Listwords = "..."; Stream longWords = words.stream().filter(w -> w.length()>12);//与1.1部分中的语句一致 //filter这的引元是Predicate ,即从 T 到 boolean 的函数
2.这部分将获取到的内容进行小写重组
StreamlowerCaseWords = words.stream().map(String::toLowerCase);
当然在这用了map方法,通常用lambda表达式即可.
3.存入获取到内容的首字母
StreamfirstLetters = words.stream().map(s -> s.subString(0,1));
在使用map时,会有一个函数应用到每个元素上,并且其结果是包含了应用该函数后所产生所有结果的流。现在,假设有一个函数,返回的不是一个值,而是一个包含多个值的流。
例如:(展示的方法将字符串转换为字符串流,即一个个的编码点);
public static StreamcodePoints(String s){ var result = new ArrayList (); int a = 0; while(i < s.length()){ int j = s.offsetByCodePoints(i,1); result.add(s.subString(i,j)) i = j; } return result.stream(); }
这个方法可以正确地处理需要用两个char值来表示Unicode字符
例如,codePoints("boat")的返回值是["b","o","a","t"];
将上方方法映射到一个字符串上
Stream> result = words.stream().map(w -> codePoints(w));
假如[...["A","B"],["C","D"]...]这样的流想转化为[...["A","B","C","D"]...]这样的,可以使用flatMap方法而不是map方法
Stream★★ 在流之外也会发现有flatMap方法因为它是计算机科学中的一种通用的概念.flatResult = words.stream().flatMap(w -> codePoints(w));
节后复习:
假设有一个泛型 G( 例如 Stream ),以及将某种类型 T 转换为 G 的函数 f 和将类型 u 转换为 G
可以通过flatMap来组合它们,即首先应用f,然后应用g,那么以上即为★单子论的关键概念.
1.产生一个流,它包含当前流中所有满足谓词 (参数) 条件的元素.
Streamfilter(Predicate super T> predicate);
2.产生一个流,它包含将mapper应用于当前流中所有元素所产生的结果
Stream Map(Function super T , ? extends R> mapper)
3.产生一个流,它是通过将mapper应用于当前流中所有元素所产生的结果连接到一起而获得的
(注意,这里每一个结果都是一个流)
Stream flatMap(Funciton< ? super T , ? extends Stream extends R> > mapper)
1.4 抽取子流和组合流
1.调用stream.limit(n)会返回一个新的流,它在n个元素后结束(如果原来的流比n短,那么就会在该流结束时结束)(括号内填的大小小于整体流的大小时跳出)这个方法对于裁剪无限流的尺寸时特别有用
Streamrandoms = Stream.generate(Math::random).limit(100); //会产生一个包含100个随机数的流
2.调用`stream.skip(n)`正好相反,它会丢弃前`n`个元素,因为按照`split`的工作方式,假如第一个元素无用:
Streamwords = Stream.of(contents.split("//PL+")).skip(1); //第一个元素无用时,跳过
3.stream.takeWhile(predicate)调用会在谓词 (参数) 为真true时获取流中所有元素后停止
假如使用上一部分的codePoints方法分割字符并收集;(整体和if有点相似,为true则存入)
StreaminitialDigits = codePoints(str).takeWhile(s ->"0...9".contain(s)); //开头包含0...9的
stream.dropWhile(predicate)则正好相反,条件为true时丢弃元素.
StreamwithoutInitialWhiteSpace = codePoints(str).dropWhile(s -> s.trim().length=0) //开头为空格的,通过trim进行获取
4.可以用静态`concat`方法将两个流拼接
Streamcombined = Stream.concat(codePoints("Hello"),codePoints("World"));
输出则为:["H","e","l","l","o","W","o","r","l","d"] codePoints方法在1.3部分内.
抽取子流和组合流总结
Stream结尾limit(long maxSize);//产生一个流,其中包含了括号内最初的元素个数 Stream skip(long n); //产生一个流,包含除了条件内的元素个数外的所有元素 ============================================================================ Stream takeWhile(Predicate super T> predicate); //产生一个流,包含所有满足条件的元素 Stream dropWhile(Predicate super T> predicate); //产生一个流,排除不满足谓词(参数)条件的元素之外的所有元素 ============================================================================ static Stream concat(Stream extends T> a , Stream extends T> b) //产生一个流,它的元素是a的元素后面跟着b的元素(和一般所知的concat基本一致,不过是关于流的拼接)
以上为Java8的流库的部分特性以及示例,还没有总结完毕,后续会保持该文章持续更新。
本文多为OGtwelve学习高级特性时所总结,创作不易,点个赞点个收藏都万分感谢哦
文章与OGtwelve个人网站www.ogtwelve.com.cn保持同步更新,再次感谢大家的点赞收藏;
在未来的日子里要和各位一起熠熠生辉 Share & Learn !



