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