您好,登錄后才能下訂單哦!
這篇文章主要介紹“Java8 Stream API有什么作用”,在日常操作中,相信很多人在Java8 Stream API有什么作用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java8 Stream API有什么作用”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
為集合而生,簡化集合操作、支持集合并發(fā)操作。
String contents = new String(Files.readAllBytes(Paths.get(alice.txt)), StandardCharsets.UTF_8); List<String> words = Arrays.asList(contents.split("[\\P{L}]+"));
int count = 0; for (String w : words) { if (w.length() > 12) count++; }
long count = words.stream().filter(w -> w.length() > 12).count(); // 并發(fā)統(tǒng)計很容易,如下只需要將stream()換成parallelStream()即可: long count = words.parallelStream().filter(w -> w.length() > 12).count();
Stream自己不會存儲元素,元素被存放于底層集合中或者根據(jù)需要被產(chǎn)生出來;
Stream操作符不會改變源Stream對象,它會返回一個持有結(jié)果新Stream對象;
Stream操作符可能是延遲執(zhí)行的,這意味著它們會等到需要結(jié)果的時候才執(zhí)行。例如只需要前5個長單詞,那么filter方法將在第5次匹配后停止過濾。
創(chuàng)建一個Stream(從集合、數(shù)組、迭代器、生成器等數(shù)據(jù)結(jié)構(gòu));
通過一個或者多個Stream操作符,將初始Stream轉(zhuǎn)換為另一個Stream;
使用一個Stream終止操作符來產(chǎn)生一個結(jié)果,該操作會強制它之前的操作立即執(zhí)行,且在此之后,該Stream就不會再被使用了。
集合:Collection的stream()方法,會產(chǎn)生一個Stream對象;
數(shù)組:Stream.of(T... values) 接受一個可變長度參數(shù)列表,參數(shù)可以是一個數(shù)組或者多個同類型獨立元素,產(chǎn)生一個Stream對象;
Arrays.stream(array, from, to)將數(shù)組的一部分轉(zhuǎn)化為Stream;
空Stream:
Stream<String> silence = Stream.empty();
無限Stream:
Stream<String> echos = Stream.generate(() -> "Echo");
Stream<Double> randoms = Stream.generate(Math::random);
Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));
JDK示例:
Stream<String> words = Pattern.compile("[\\P{L}]+").splitAsStream(contents);
Stream<String> lines = Files.lines(path);
filter:過濾元素,并產(chǎn)生一個新的Stream對象;
map:轉(zhuǎn)換;
flatMap:一對多轉(zhuǎn)換,即每個元素映射的結(jié)構(gòu)都是另一個Stream,最終將所有Stream合并為一個Stream;
// filter:只獲取長單詞 List<String> wordList = ... ; Stream<String> words = wordList.stream(); Stream<String> longWords = words.filter(w -> w.length() > 12);
// map:將所有單詞轉(zhuǎn)換為小寫 Stream<String> lowercaseWords = words.map(w -> w.toLowerCase()); // 或者使用方法引用 Stream<String> lowercaseWords = words.map(String::toLowerCase()); // 獲取每個單詞的首字母 Stream<Character> firstChars = words.map(w -> w.charAt(0));
// flatMap:獲取每個單詞中的字母 Stream<Character> letters = words.flatMap(w -> Arrays.stream(s.toCharArray()));
Stream.limit(n),返回一個包含源Stream前n個元素的新Stream,如果源Stream長度m小于n則返回前m個元素;
Stream.skip(n),丟棄掉源Stream的前n個元素,返回包含剩余元素的新Stream;
組合兩個Stream為一個新的Stream,假設(shè)stream1和stream2為兩個Character的Stream:
Stream<Character> combined = Stream.concat(stream1, stream2);
Stream.peek(action),會產(chǎn)生另一個與源Stream具有相同元素的Stream,但是在獲取每個元素時,都會調(diào)用action參數(shù)指定的函數(shù),這樣是為了便于調(diào)試。
Stream的filter和map、flatMap等操作都是無狀態(tài)的轉(zhuǎn)換,因為在轉(zhuǎn)換每一個元素時無需考慮之前轉(zhuǎn)換過的元素;
Stream的distinct操作,會根據(jù)源Stream中的元素,返回一個元素順序相同,但是沒有重復(fù)元素的新Stream,是有狀態(tài)的轉(zhuǎn)換,因為它在轉(zhuǎn)換每個元素時都需要檢查該元素是否之前已讀取過。
Stream的聚合操作會將Stream聚合成為一個值,以便程序中使用,例如Stream.count()、Stream.max()、Stream.min()、Stream.findFirst()、Stream.findAny()等。
聚合方法都是終止操作,執(zhí)行后流就關(guān)閉了,不能再應(yīng)用其它操作了。
Optional<String> longest = words.max(String::compareToIgnoreCase); if (longest.isPresent()) { Sysout.out.println(longest.get()); }
Optional<String> startWithQ = words.filter(s -> s.startsWith("Q")).findFirst();
// 并行提高執(zhí)行效率 Optional<String> startWithQ = words.parallel().filter(s -> s.startsWith("Q")).findAny();
// 并行提高執(zhí)行效率 Optional<String> startWithQ = words.parallel().anyMatch(s -> s.startsWith("Q"));
一個Stream對象在執(zhí)行終止操作后,就不能再執(zhí)行其他操作了。
Optional<T>
對象是對T類型對象的封裝,或者表示不是任何對象。
Optional類本身實現(xiàn)非常簡單,常用操作有:
of(T value)
ofNullable(T value)
isPresent()
ifPresent(consumer)
orElse(T other)
orElseGet(Supplier other)
map(Function mapper)
flatMap(Function mapper)用于組合可選函數(shù),例如,如果f()返回
Optional<T>
,T有一個返回Optional<U>
的方法g(),就可以組合調(diào)用:Optional<U> op = f().flatMap(T::g)
用法和其余的看源碼最直觀。
聚合,即將Stream中的元素聚合成為一個值,例如:求和、求積、計數(shù)、字符串追加、最大值、最小值、并積、交積等,只要操作數(shù)x,y,z之間有一個操作op,滿足(x op y) op z = x op (y op z),那么op操作就是可聚合的;
// 求和 Stream<Integer> values = ...; Optional<Integer> sum = values.reduce((x, y) -> x + y); // 如果有一個標(biāo)識e,使得e op x = x,那么標(biāo)識e就可以作為計算的起點,對于加法來說0就是這個標(biāo)識,所以另外一種形式: Optional<Integer> sum = values.reduce(0, (x, y) -> x + y); // 或者 Optional<Integer> sum = values.reduce(0, Integer::sum);
// 求字符串Stream中所有字符串總長度 Stream<String> words = ...; Optional<Integer> sum = words.reduce(0, (total, word) -> total + word.length(), (total1, total2) - > total1 + total2); // 第二個參數(shù)accumulator,是為了聚合計算; // 第三個參數(shù)combiner,是為了并行聚合計算后,對并行結(jié)果進(jìn)行再聚合;
流處理完成以后,我們對于處理結(jié)果有兩種用途:
執(zhí)行聚合操作,將整個Stream聚合成為一個值,例如:sum、count、max、min等;
收集Stream處理結(jié)果,獲取Stream中的每個元素,或打印、或轉(zhuǎn)儲、或執(zhí)行其他計算;
第二種“收集Stream處理結(jié)果”,由兩種操作:
collect,兩種方式:
<R, A> R collect(Collector<? super T, A, R> collector);
// 使用一個現(xiàn)成的預(yù)定義的Collector進(jìn)行收集,Collectors工具類還為各種常用的收集類型提供了各個工廠方法。示例1:
Stream<String> stream = ...;
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
TreeSet<String> treeSet = stream.collect(Collectors.toCollection(TreeSet::new));
//控制得到的set類型
String result = stream.collect(Collectors.join());
//將Stream中所有字符串拼接
String result = stream.collect(Collectors.join(“, ”));
//將Stream中所有字符串拼接且以", " 分隔
// 將Stream結(jié)果聚合成為一個包含總和、最大值、最小值、平均值的結(jié)果,可以使用Collectors.summaring{Int | Long | Double}方法中的一種,這些方法會接受一個將Stream對象的元素映射為一個數(shù)字的函數(shù),并產(chǎn)生一個{Int | Long | Double}SummaryStatistics類型的結(jié)果,其中包含了獲取總和、最大值、最小值、平均值:
IntSummaryStatistics summary = words.collect(Collectors.summarizingInt(String::length));
double arerageWordLength = summary.getAverage();
double maxWordLength = summary.getMax();
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
// 自定義supplier、accumulator、combinersupplier – a function that creates a new result container. For a parallel execution, this function may be called multiple times and must return a fresh value each time.
accumulator – an associative, non-interfering, stateless function for incorporating an additional element into a result.
combiner – an associative, non-interfering, stateless function for combining two values, which must be compatible with the accumulator function.
示例:
Stream<String> stream = ...;
HashSet<String> result = stream.collect(HashSet::new, HashSet::add, HashSet::addAll);
遍歷獲取元素,兩種方式:
void forEach(Consumer<? super T> action);
非順序遍歷Stream元素,對于并行stream則并行遍歷,順序無法保證,但能提升遍歷效率
void forEachOrdered(Consumer<? super T> action);
順序遍歷元素,保證順序性,但是犧牲了性能
將結(jié)果收集到Map中:
// 普通toMap Collector,如果鍵沖突則會跑出IllegalStateException Stream<Person> persons = ...; Map<String, String> idToName = persons.collect(Collectors.toMap(Person::getId, Person::getName)); Map<String, Person> idToPerson = persons.collect(Collectors.toMap(Person::getId, Function.identity()));
// 定義鍵沖突解決策略,保留舊值 Stream<Locale> locales = Stream.of(Locale.getAvailableLocales()); Map<String, String> languages = locales.collect(Collectors.toMap(lan - > lan.getDisplayCountry(), lan -> lan.getDisplayLanguage(), (existingValue, newValue) - > existingValue)); Map<String, Person> idToPerson = persons.collect(Collectors.toMap(Person::getId, Function.identity()));
// 定義鍵沖突解決策略,以Set方式保留所有值 Stream<Locale> locales = Stream.of(Locale.getAvailableLocales()); Map<String, String> languages = locales.collect(Collectors.toMap(lan - > lan.getDisplayCountry(), lan -> Collections.singleton(lan.getDisplayLanguage(), (existingValue, newValue) - > { Set<String> mergedSet = new HashSet<>(existingValue); mergedSet.addAll(newValue); return mergedSet; }; ));
// 通過指定第四個參數(shù),將結(jié)果收集到TreeMap中去 Stream<Locale> locales = Stream.of(Locale.getAvailableLocales()); Map<String, String> languages = locales.collect(Collectors.toMap(lan - > lan.getDisplayCountry(), lan -> lan.getDisplayLanguage(), (existingValue, newValue) - > existingValue), TreeMap::new));
Collectors.groupingBy
Stream收集到Map過程中,通過分組,來簡化相同鍵的合并問題,示例如下:
Map<String, List<Locale>> countryToLocales = locales.collect(Collectors.groupingBy(Locale::getCountry));
函數(shù)Locale::getCountry是進(jìn)行分組的分類函數(shù)。Collectors.partitioningBy
當(dāng)分組函數(shù)是一個Prdicate函數(shù)時,即要將Stream中的元素分為是/非兩組時,使用partitioningBy會更高效些,示例如下:
Map<Boolean, List<Locale>> englishAndOtherLocales = locales.collect(Collectors.partitioningBy(lan -> lan.getLanguage().equals("en")));
將元素分為兩組:一組使用英語,一組使用其他語言。獲取英語分組列表:
List<Locale> englishLocales = englishAndOtherLocales.get(true);
Collectors.groupingByConcurrent
會獲取一個并發(fā)Map,當(dāng)用于并行流時可以并發(fā)地插入值。這與toConcurrentMap方法完全類似。
downstream
方法groupingBy會產(chǎn)生一個值為列表的map的對象,如果希望對這個列表進(jìn)行轉(zhuǎn)換,例如轉(zhuǎn)為Set,則可基于downstream實現(xiàn),例如:
Map<String, Set<Locale>> countryToLocaleSet = locales.collect(Collectors.groupingBy(Locale::getCountry, Collectors.toSet()));
其他downstream
counting()
Map<String, Long> countryToLocaleCount = locales.collect(Collectors.groupingBy(Locale::getCountry, Collectors.counting()));
// 計算每個國家有多少種語言summing{Int | Long | Double}(ToIntFunction mapper)
Map<String, Integer> stateToCityPopucation = cities.collect(Collectors.groupingBy(City::getState, Collectors.summingInt(City::getPopulation())));
// 計算每個下屬所有城市人數(shù)總和maxBy(Comparator comparator) 和minBy(Comparator comparator)
Map<String, City> stateToLargestCity = cities.collect(Collectors.groupingBy(City::getState, Collectors.maxBy(Comparator.comparing(City::getPopulation))));
// 計算每個州中人口最多的城市mapping(Function mapper, Collector downstream)
Map<String, Optional<String>> stateToLargestCityName = cities.collect(Collectors.groupingBy(City::getState, Collectors.mapping(City::getName, Collectors.maxBy(Comparator.comparing(String::length)))))
// 找出每個州中,名字最長的城市名稱
Map<String, Set<String>> countryToLanguages = locales.collect(Collectors.groupingBy(Locale::getDisplayCountry, mapping(Locale::getDisplayLanguage, Collectors.toSet())))
如果grouping或者mapping函數(shù)的返回結(jié)果是int、long、double,可以將元素收集到summaryStatistics對象中:
Map<String, IntSummaryStatistics> statistics = locales.collect(Colloctors.groupingBy(Locale::getState, summarizingInt(City::getPopulation)));
reducing方法可對downstream元素進(jìn)行一次普通聚合
Collector reducing(BinaryOperator op)
Collector reducing(identity, BinaryOperator op)
Collector reducing(identity, Function mapper, BinaryOperator op)
示例:
Map<String, String> stateToCityNames = cities.collect(groupingBy(City:getState, reducing("", City::getName, (s, t) -> s.length == 0 ? t : s + "," + t)))
等價于:
Map<String, String> stateToCityNames = cities.collect(groupingBy(City:getState, mapping( City::getName, joining(","))));
背景
將整型收集到一個
Stream<Integer>
的流中,需要將每個整數(shù)封裝成一個包裝對象,這是一個低效的做法,double/float/long/short/byte/boolean也一樣,所以,Stream API專門設(shè)計了IntStream、LongStream、DoubleStream用于存放基本數(shù)據(jù)類型;
IntStream:用于存放int、short、char、byte和boolean類型的值;
LongStream:用于存放long型的值;
DoubleStream:用于存放double、float類型的值;
示例
創(chuàng)建IntStream,使用IntStream.of和Arrays.stream
IntStream stream = IntStream.of(1,1,2,3,5);
int[] array = new int[]{1,1,2,3,5};
stream = Arrays.stream(array, 0, array.length);
IngStream和LongStream的range和rangeClosed方法:
IntStream zeroTo99 = IntStream.range(0, 100);
//不包括上線100
IntStream zeroTo100 = IntStream.rangeClosed(0, 100);
//包括上線100
對于對象流,可使用mapToInt、mapToLong、mapToDouble轉(zhuǎn)換為對應(yīng)的原始類型的流。例如:
Stream<String> words = ...;
IntStream lengths = words.mapToInt(String::length);
原始類型流的常用方法:
sum、max、min、average、summaryStatistics等。
Random類的ints、longs、doubles分別產(chǎn)生對應(yīng)的原始類型隨機(jī)數(shù)字流。
默認(rèn)情況下,除了Collection.parallelStream()外,流操作創(chuàng)建的都是串行流。
開啟并行流(在終止方法執(zhí)行前)
Collection.parallelStream()、Stream.parallel()。
當(dāng)無需考慮Stream元素順序時,Stream.unordered() 會使并行操作更好地執(zhí)行,例如distinct(),因為對于一個有序的流,distinct()操作總會保留所有相同元素的第一個,同樣的操作還有l(wèi)imit();
到此,關(guān)于“Java8 Stream API有什么作用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。