Stream流由淺到深–持續更新中
從迭代到流操作
? 在處理集合時,我們通常會迭代遍歷他的元素,并在每個元素上執行某項操作時,
普通迭代操作
Path path = Paths.get("E:\\JavaCode\\fanshe\\src\\main\\resources\\StreamTest.txt");
String contents = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
List<String> words=ArrayList.asList(contents.split("\\PL+"));
long count = 0;
for(String w : words )
{
if(w.length()>12) count++;
}
流式操作
long count = words.stream()
.filter(w -> w.length() > 12)
.count();
? 流的版本比回圈也好更易于閱讀,因此我們不需要掃描整個代碼去查找過濾器和計數器操作,方法名就可以告訴我們其代碼意欲何為,而且回圈需要非常詳細的指定操作的順序,而流卻能夠以其想要的任何方式來調度這些操作,只要結果是正確的即可,
? 僅以stream修改為parallelStream就可以讓流庫以并行方式來執行過濾和計數,
long count=words.parallelStream()
.filter(w -> w.length()>12)
.count();
? 流遵循了“做什么而非怎么做”的原則,在示例中,我們沒有指定該操作以什么順序或者在哪個執行緒中執行,相比之下,迭代則要確切指出計算該如何作業,因此就喪失了進行優化的機會,流表面看氣力啊和集合很類似,都可以讓我們轉換和獲取資料,但是他們之間存在著顯著的差異:
1.流并不存盤其元素
2.流的操作不會修改其資料源
3.流的操作是盡可能惰性執行的
流的創建
public static <T> void show(String title, Stream<T> stream) {
final int SIZE = 10;
List<T> firstElements = stream.limit(SIZE + 1).collect(Collectors.toList());
System.out.print(title + ": ");
for (int i = 0; i < firstElements.size(); i++) {
if (i > 0) System.out.print(", ");
if (i < SIZE) System.out.print(firstElements.get(i));
else System.out.print("...");
}
System.out.println();
}
public static void main(String[] args) throws IOException {
Path path = Paths.get("E:\\JavaCode\\fanshe\\src\\main\\resources\\StreamTest.txt");
String contents = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
Stream<String> word = Stream.of(contents.split("\\PL+"));
show("word", word);
Stream<String> song = Stream.of("gently", "down", "the", "stream");
show("song", song);
Stream<String> silence = Stream.empty();
show("silence", silence);
Stream<String> echos = Stream.generate(() -> "echo");
show("echos", echos);
Stream<Double> random = Stream.generate(Math::random);
show("random", random);
Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));
show("integers", integers);
Stream<String> wordsAnotherWay = Pattern.compile("\\PL+").splitAsStream(contents);
show("wordsAnotherWay", wordsAnotherWay);
try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) {
show("lines", lines);
}
}
上面的示例展示了各種創建流的方式
- 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產生的值,等等,
- static < T> Stream< T> stream(T[] array, int startInclusive, int endExclusive) 產生一個流,他的元素是由陣列中指定范圍內的元素構成的
- Stream< String> splitAsStream(CharSequence input) 產生一個流,他的元素是由輸入總由該模式界定的部分
- static Stream< String> lines(Path path)
- static Stream< String> lines(Path path, Charset cs) 產生一個流,他的元素是指定檔案中的行,該檔案的字符集為UTF-8,或者為指定的字符集
- T get() 提供一個值
filter、map和flatMap方法
流的轉化會產生一個新的流,他的元素派生自另一個流元素,fileter轉化會產生一個流,他的元素與某種條件相匹配,
List<String> wordList= ...;
Stream<String> longWords= wordList.stream().filter(w -> w.length() > 12);
fileter的引元是Predicate,即從T到boolean的函式,通常我們想要按照某種方式來轉化流中的值,此時,可以使用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(“boat”)的回傳值是流[“b”,“o”,“a”,“t”]
Stream<Stream<String>> result=words.stream().map(w -> letters(w));
執行本行代碼就會得到一個包含流的流,類似于[…[“y”,“o”,“u”,“r”],[“b”,“o”,“a”,“t”],…],為了將其攤平為字母流[…,“y”,“o”,“u”,“r”,“b”,“o”,“a”,“t”,…],可以使用flatMap方法而不是map方法
Stream<String> flatResult = words.stream().flatMap(w -> letters(w));
//Calls letters on each word and flattens the results
注意:在流之外的類中你也會發現flatMap方法,因為他是計算機科學中的一種通用概念,假設我們有一個泛型G(例如Stream),以及將某種型別T轉換為G< U>的函式f和將型別U轉換為G< V>的函式G,然后我們可以通過使用flatMap來組合他們,即首先應用f,然后應用g,這是單子論的關鍵概念,但是不必擔心,我們無需了解任何有關單子論的知識就可以使用flatMap,
- Stream< T> filter(Predicate<? super T> predicat) 產生一個流,他包含當前劉中所有滿足斷言條件的元素
- < R> Stream< R> map<Function<? super T, ? extends R> mapper) 產生一個流,他包含將mapper應用于當前流中所有元素產生的結果
- < R> Stream< R> flatMap<Function<? super T, ? extends Stream<? extends R>> mapper) 產生一個流,他是通過將mapper應用于當前流中所有元素所產生的結果連接到一起而獲得的,(注意,這里的每個結果都是一個流)
抽取子流和連接流
呼叫stream.limit(n) 會回傳一個新的流,他在n個元素之后結束(如果原來的流更短,那么就會在流結束時結束),這個方法對于裁剪無限流的尺寸會顯得特別有用,下面這個示例方法會產生一個包含100個亂數的流,
Stream<Double> ramdoms = Stream.generate(Math::random).limit(100);
呼叫stream.skip(n) 正好相反:他會丟棄前n個元素,這個方法在將文本分隔為單詞時會顯得很方便,因為按照split方法的作業方式,第一個元素是沒有什么用的空字串,
Stream<String> words = Stream.of(contents.split("\\PL+")).skip(1);
通過limt和skip我們就得到了兩個流,這個時候如果我們需要將兩個流進行合并,那么我們可以用Stream類靜態的concat方法將兩個流連接起來,
Stream<String> combined = Stream.concat(letters("Hello"),letters("world"));
//Yields the stream ["H","e","l","l","o","w","o","r","l","d"]
當然,第一個流不能是無限流,否則第二個流永遠都不會得到處理的機會
- Stream< T> limit(long maxSize) 產生一個流,其中包含了當前流中最初的maxSize個元素
- Stream< T> skip(long n) 產生一個流,他的元素是當前流中出了前n個元素之外的所有元素
- static < T> Stream< T> concat(Stream<? extends T> a,Stream<? extends T> b) 產生一個流,他的元素是a的元素后面跟著b的元素
其他流轉換
簡單約簡
Optional型別
收集結果收集到映射表中
群組和磁區
下游收集齊
簡約操作
基本型別流
并行流
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/242002.html
標籤:java
