java8-02-Stream-API
[TOC]
0 Stream簡介
家庭住址 :java.util.stream.Stream<T>
出生年月:Java8問世的時候他就來到了世上
主要技能:那可以吹上三天三夜了……
-
主要特征
不改變輸入源
中間的各種操作是lazy的(惰性求值、延遲操作)
只有當開始消費流的時候,流才有意義
隱式迭代
……
總體感覺,Stream相當于一個進化版的Iterator。Java8源碼里是這么注釋的:
A sequence of elements supporting sequential and parallel aggregate operations
可以方便的對集合進行遍歷、過濾、映射、匯聚、切片等復雜操作。最終匯聚成一個新的Stream,不改變原始數據。并且各種復雜的操作都是lazy的,也就是說會盡可能的將所有的中間操作在最終的匯聚操作一次性完成。
比起傳統的對象和數據的操作,Stream更專注于對流的計算,和傳說中的函數式編程有點類似。
他具體進化的多牛逼,自己體驗吧。
給一組輸入數據:
List<Integer> list = Arrays.asList(1, null, 3, 1, null, 4, 5, null, 2, 0);求輸入序列中非空奇數之和,并且相同奇數算作同一個。
在lambda還在娘胎里的時候,為了實現這個功能,可能會這么做
當lambda和Stream雙劍合璧之后:
1 獲取Stream
從lambda的其他好基友那里獲取Stream
從1.8開始,接口中也可以存在 default 修飾的方法了。
java.util.Collection<E> 中有如下聲明:
public interface Collection<E> extends Iterable<E> {// 獲取普通的流default Stream<E> stream() {return StreamSupport.stream(spliterator(), false);}// 獲取并行流default Stream<E> parallelStream() {return StreamSupport.stream(spliterator(), true);} }java.util.Arrays中有如下聲明:
public static <T> Stream<T> stream(T[] array) {return stream(array, 0, array.length);}public static IntStream stream(int[] array) {return stream(array, 0, array.length);}// 其他類似的方法不再一一列出示例
List<String> strs = Arrays.asList("apache", "spark"); Stream<String> stringStream = strs.stream();IntStream intStream = Arrays.stream(new int[] { 1, 25, 4, 2 });通過Stream接口獲取
注意:Stream.iterate()和 Stream.generate()生成的是無限流,一般要手動limit 。
2 轉換Stream
流過濾、流切片
這部分相對來說還算簡單明了,看個例子就夠了
// 獲取流 Stream<String> stream = Stream.of(//null, "apache", null, "apache", "apache", //"github", "docker", "java", //"hadoop", "linux", "spark", "alifafa");stream// 去除null,保留包含a的字符串.filter(e -> e != null && e.contains("a"))//.distinct()// 去重,當然要有equals()和hashCode()方法支持了.limit(3)// 只取滿足條件的前三個.forEach(System.out::println);// 消費流map/flatMap
Stream的map定義如下:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);也就是說,接收一個輸入(T:當前正在迭代的元素),輸出另一種類型(R)。
Stream.of(null, "apache", null, "apache", "apache", //"hadoop", "linux", "spark", "alifafa")//.filter(e -> e != null && e.length() > 0)//.map(str -> str.charAt(0))//取出第一個字符.forEach(System.out::println);sorted
排序也比較直觀,有兩種:
// 按照元素的Comparable接口的實現來排序 Stream<T> sorted();// 指定Comparator來自定義排序 Stream<T> sorted(Comparator<? super T> comparator);示例:
List<HouseInfo> houseInfos = Lists.newArrayList(//new HouseInfo(1, "恒大星級公寓", 100, 1), //new HouseInfo(2, "匯智湖畔", 999, 2), //new HouseInfo(3, "張江湯臣豪園", 100, 1), //new HouseInfo(4, "保利星苑", 23, 10), //new HouseInfo(5, "北顧小區", 66, 23), //new HouseInfo(6, "北杰公寓", null, 55), //new HouseInfo(7, "保利星苑", 77, 66), //new HouseInfo(8, "保利星苑", 111, 12)// );houseInfos.stream().sorted((h1, h2) -> {if (h1 == null || h2 == null)return 0;if (h1.getDistance() == null || h2.getDistance() == null)return 0;int ret = h1.getDistance().compareTo(h2.getDistance());if (ret == 0) {if (h1.getBrowseCount() == null || h2.getBrowseCount() == null)return 0;return h1.getBrowseCount().compareTo(h2.getBrowseCount());}return ret; });3 終止/消費Stream
條件測試、初級統計操作
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);// 是不是所有元素都大于零 System.out.println(list.stream().allMatch(e -> e > 0)); // 是不是存在偶數 System.out.println(list.stream().anyMatch(e -> (e & 1) == 0)); // 是不是都不小于零 System.out.println(list.stream().noneMatch(e -> e < 0));// 找出第一個大于等于4的元素 Optional<Integer> optional = list.stream().filter(e -> e >= 4).findFirst(); // 如果存在的話,就執行ifPresent中指定的操作 optional.ifPresent(System.out::println);// 大于等于4的元素的個數 System.out.println(list.stream().filter(e -> e >= 4).count()); // 獲取最小的 System.out.println(list.stream().min(Integer::compareTo)); // 獲取最大的 System.out.println(list.stream().max(Integer::compareTo)); // 先轉換成IntStream,max就不需要比較器了 System.out.println(list.stream().mapToInt(i -> i).max());reduce
這個詞不知道怎么翻譯,有人翻譯為 規約 或 匯聚。
反正就是將經過一系列轉換后的流中的數據最終收集起來,收集的同時可能會反復 apply 某個 reduce函數。
reduce()方法有以下兩個重載的變體:
// 返回的不是Optional,因為正常情況下至少有參數identity可以保證返回值不會為null T reduce(T identity, BinaryOperator<T> accumulator);<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);示例:
// 遍歷元素,反復apply (i,j)->i+j的操作 Integer reduce = Stream.iterate(1, i -> i + 1)//1,2,3,...,10,....limit(10)//.reduce(0, (i, j) -> i + j);//55Optional<Integer> reduce2 = Stream.iterate(1, i -> i + 1)//.limit(10)//.reduce((i, j) -> i + j);collect
該操作很好理解,顧名思義就是將Stream中的元素collect到一個地方。
最常規(最不常用)的collect方法
一個參數的版本
Collector接口(他不是函數式接口,沒法使用lambda)的關鍵代碼如下:
public interface Collector<T, A, R> {/****/Supplier<A> supplier();/*** */BiConsumer<A, T> accumulator();/*** */BinaryOperator<A> combiner();/****/Function<A, R> finisher();/*** */Set<Characteristics> characteristics();}先來看一個關于三個參數的collect()方法的例子,除非特殊情況,不然我保證你看了之后這輩子都不想用它……
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); ArrayList<Integer> ret1 = numbers.stream()//.map(i -> i * 2)// 擴大兩倍.collect(//() -> new ArrayList<Integer>(), //參數1(list, e) -> list.add(e), //參數2(list1, list2) -> list1.addAll(list2)//參數3 );/**** <pre>* collect()方法的三個參數解釋如下:* 1. () -> new ArrayList<Integer>() * 生成一個新的用來存儲結果的集合* 2. (list, e) -> list.add(e)* list:是參數1中生成的新集合* e:是Stream中正在被迭代的當前元素* 該參數的作用就是將元素添加到新生成的集合中* 3. (list1, list2) -> list1.addAll(list2)* 合并集合* </pre>***/ret1.forEach(System.out::println);不使用lambda的時候,等價的代碼應該是這個樣子的……
List<Integer> ret3 = numbers.stream()//.map(i -> i * 2)// 擴大兩倍.collect(new Supplier<List<Integer>>() {@Overridepublic List<Integer> get() {// 只是為了提供一個集合來存儲元素return new ArrayList<>();}}, new BiConsumer<List<Integer>, Integer>() {@Overridepublic void accept(List<Integer> list, Integer e) {// 將當前元素添加至第一個參數返回的容器中list.add(e);}}, new BiConsumer<List<Integer>, List<Integer>>() {@Overridepublic void accept(List<Integer> list1, List<Integer> list2) {// 合并容器list1.addAll(list2);}});ret3.forEach(System.out::println);是不是被惡心到了……
同樣的,用Java調用spark的api的時候,如果沒有lambda的話,比上面的代碼還惡心……
順便打個免費的廣告,可以看看本大俠這篇使用各種版本實現的Spark的HelloWorld: http://blog.csdn.net/hylexus/...,來證明一下有lambda的世界是有多么幸福……
不過,當你理解了三個參數的collect方法之后,可以使用構造器引用和方法引用來使代碼更簡潔:
ArrayList<Integer> ret2 = numbers.stream()//.map(i -> i * 2)// 擴大兩倍.collect(//ArrayList::new, //List::add, //List::addAll// );ret2.forEach(System.out::println);Collectors工具的使用(高級統計操作)
上面的三個和一個參數的collect()方法都異常復雜,最常用的還是一個參數的版本。但是那個Collector自己實現的話還是很惡心。
還好,常用的Collect操作對應的Collector都在java.util.stream.Collectors 中提供了。很強大的工具……
以下示例都是對該list的操作:
List<HouseInfo> houseInfos = Lists.newArrayList(//new HouseInfo(1, "恒大星級公寓", 100, 1), // 小區ID,小區名,瀏覽數,距離new HouseInfo(2, "匯智湖畔", 999, 2), //new HouseInfo(3, "張江湯臣豪園", 100, 1), //new HouseInfo(4, "保利星苑", 111, 10), //new HouseInfo(5, "北顧小區", 66, 23), //new HouseInfo(6, "北杰公寓", 77, 55), //new HouseInfo(7, "保利星苑", 77, 66), //new HouseInfo(8, "保利星苑", 111, 12)// );好了,開始裝逼之旅 ^_^ ……
提取小區名
最值
總數、總和
均值
統計信息
分組
分區
總結
以上是生活随笔為你收集整理的java8-02-Stream-API的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 诺基亚AirScale支持低频段和高频段
- 下一篇: shell 查出文件并复制到另一个文件夹