Stream API有哪些中間操作?看完你也可以吊打面試官?。?/h1>
作者個(gè)人研發(fā)的在高并發(fā)場(chǎng)景下,提供的簡(jiǎn)單、穩(wěn)定、可擴(kuò)展的延遲消息隊(duì)列框架,具有精準(zhǔn)的定時(shí)任務(wù)和延遲隊(duì)列處理功能。自開(kāi)源半年多以來(lái),已成功為十幾家中小型企業(yè)提供了精準(zhǔn)定時(shí)調(diào)度方案,經(jīng)受住了生產(chǎn)環(huán)境的考驗(yàn)。為使更多童鞋受益,現(xiàn)給出開(kāi)源框架地址:
https://github.com/sunshinelyz/mykit-delay
Stream的中間操作
多個(gè)中間操作可以連接起來(lái)形成一個(gè)流水線,除非流水線上觸發(fā)終止操作,否則中間操作不會(huì)執(zhí)行任何的處理!而在終止操作時(shí)一次性全部處理,稱為“惰性求值” 。Stream的中間操作是不會(huì)有任何結(jié)果數(shù)據(jù)輸出的。
Stream的中間操作在整體上可以分為:篩選與切片、映射、排序。接下來(lái),我們就分別對(duì)這些中間操作進(jìn)行簡(jiǎn)要的說(shuō)明。
篩選與切片
這里,我將與篩選和切片有關(guān)的操作整理成如下表格。
接下來(lái),我們列舉幾個(gè)簡(jiǎn)單的示例,以便加深理解。
為了更好的測(cè)試程序,我先構(gòu)造了一個(gè)對(duì)象數(shù)組,如下所示。
- protected List<Employee> list = Arrays.asList(
- new Employee("張三", 18, 9999.99),
- new Employee("李四", 38, 5555.55),
- new Employee("王五", 60, 6666.66),
- new Employee("趙六", 8, 7777.77),
- new Employee("田七", 58, 3333.33)
- );
其中,Employee類的定義如下所示。
- @Data
- @Builder
- @ToString
- @NoArgsConstructor
- @AllArgsConstructor
- public class Employee implements Serializable {
- private static final long serialVersionUID = -9079722457749166858L;
- private String name;
- private Integer age;
- private Double salary;
- }
Employee類的定義比較簡(jiǎn)單,這里,我就不贅述了。之后的示例中,我們都是使用的Employee對(duì)象的集合進(jìn)行操作。好了,我們開(kāi)始具體的操作案例。
1.filter()方法
filter()方法主要是用于接收Lambda表達(dá)式,從流中排除某些元素,其在Stream接口中的源碼如下所示。
- Stream<T> filter(Predicate<? super T> predicate);
可以看到,在filter()方法中,需要傳遞Predicate接口的對(duì)象,Predicate接口又是個(gè)什么鬼呢?點(diǎn)進(jìn)去看下源碼。
- @FunctionalInterface
- public interface Predicate<T> {
- boolean test(T t);
- default Predicate<T> and(Predicate<? super T> other) {
- Objects.requireNonNull(other);
- return (t) -> test(t) && other.test(t);
- }
- default Predicate<T> negate() {
- return (t) -> !test(t);
- }
- default Predicate<T> or(Predicate<? super T> other) {
- Objects.requireNonNull(other);
- return (t) -> test(t) || other.test(t);
- }
- static <T> Predicate<T> isEqual(Object targetRef) {
- return (null == targetRef)
- ? Objects::isNull
- : object -> targetRef.equals(object);
- }
- }
可以看到,Predicate是一個(gè)函數(shù)式接口,其中接口中定義的主要方法為test()方法,test()方法會(huì)接收一個(gè)泛型對(duì)象t,返回一個(gè)boolean類型的數(shù)據(jù)。
看到這里,相信大家明白了:filter()方法是根據(jù)Predicate接口的test()方法的返回結(jié)果來(lái)過(guò)濾數(shù)據(jù)的,如果test()方法的返回結(jié)果為true,符合規(guī)則;如果test()方法的返回結(jié)果為false,則不符合規(guī)則。
這里,我們可以使用下面的示例來(lái)簡(jiǎn)單的說(shuō)明filter()方法的使用方式。
- //內(nèi)部迭代:在此過(guò)程中沒(méi)有進(jìn)行過(guò)迭代,由Stream api進(jìn)行迭代
- //中間操作:不會(huì)執(zhí)行任何操作
- Stream<Person> stream = list.stream().filter((e) -> {
- System.out.println("Stream API 中間操作");
- return e.getAge() > 30;
- });
我們,在執(zhí)行終止語(yǔ)句之后,一邊迭代,一邊打印,而我們并沒(méi)有去迭代上面集合,其實(shí)這是內(nèi)部迭代,由Stream API 完成。
下面我們來(lái)看看外部迭代,也就是我們?nèi)藶榈玫?/p>
- //外部迭代
- Iterator<Person> it = list.iterator();
- while (it.hasNext()) {
- System.out.println(it.next());
- }
2.limit()方法
主要作用為:截?cái)嗔?,使其元素不超過(guò)給定數(shù)量。
先來(lái)看limit方法的定義,如下所示。
- Stream<T> limit(long maxSize);
limit()方法在Stream接口中的定義比較簡(jiǎn)單,只需要傳入一個(gè)long類型的數(shù)字即可。
我們可以按照如下所示的代碼來(lái)使用limit()方法。
- //過(guò)濾之后取2個(gè)值
- list.stream().filter((e) -> e.getAge() >30 ).limit(2).forEach(System.out :: println);
在這里,我們可以配合其他得中間操作,并截?cái)嗔?,使我們可以取得相?yīng)個(gè)數(shù)得元素。而且在上面計(jì)算中,只要發(fā)現(xiàn)有2條符合條件得元素,則不會(huì)繼續(xù)往下迭代數(shù)據(jù),可以提高效率。
3.skip()方法
跳過(guò)元素,返回一個(gè)扔掉了前 n 個(gè)元素的流。若流中元素 不足 n 個(gè),則返回一個(gè)空流。與 limit(n) 互補(bǔ)。
源碼定義如下所示。
- Stream<T> skip(long n);
源碼定義比較簡(jiǎn)單,同樣只需要傳入一個(gè)long類型的數(shù)字即可。其含義是跳過(guò)n個(gè)元素。
簡(jiǎn)單示例如下所示。
- //跳過(guò)前2個(gè)值
- list.stream().skip(2).forEach(System.out :: println);
4.distinct()方法
篩選,通過(guò)流所生成元素的 hashCode() 和 equals() 去 除重復(fù)元素。
源碼定義如下所示。
- Stream<T> distinct();
旨在對(duì)流中的元素進(jìn)行去重。
我們可以如下面的方式來(lái)使用disinct()方法。
- list.stream().distinct().forEach(System.out :: println);
這里有一個(gè)需要注意的地方:distinct 需要實(shí)體中重寫hashCode()和 equals()方法才可以使用。
映射
關(guān)于映射相關(guān)的方法如下表所示。
1.map()方法
接收一個(gè)函數(shù)作為參數(shù),該函數(shù)會(huì)被應(yīng)用到每個(gè)元 素上,并將其映射成一個(gè)新的元素。
先來(lái)看Java8中Stream接口對(duì)于map()方法的聲明,如下所示。
- <R> Stream<R> map(Function<? super T, ? extends R> mapper);
我們可以按照如下方式使用map()方法。
- //將流中每一個(gè)元素都映射到map的函數(shù)中,每個(gè)元素執(zhí)行這個(gè)函數(shù),再返回
- List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
- list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf);
- //獲取Person中的每一個(gè)人得名字name,再返回一個(gè)集合
- List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList());
2.flatMap()
接收一個(gè)函數(shù)作為參數(shù),將流中的每個(gè)值都換成另 一個(gè)流,然后把所有流連接成一個(gè)流。
先來(lái)看Java8中Stream接口對(duì)于flatMap()方法的聲明,如下所示。
- <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
我們可以使用如下方式使用flatMap()方法,為了便于大家理解,這里,我就貼出了測(cè)試flatMap()方法的所有代碼。
- /**
- * flatMap —— 接收一個(gè)函數(shù)作為參數(shù),將流中的每個(gè)值都換成一個(gè)流,然后把所有流連接成一個(gè)流
- */
- @Test
- public void testFlatMap () {
- StreamAPI_Test s = new StreamAPI_Test();
- List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
- list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println);
- //如果使用map則需要這樣寫
- list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> {
- e.forEach(System.out::println);
- });
- }
- /**
- * 將一個(gè)字符串轉(zhuǎn)換為流
- */
- public Stream<Character> filterCharacter(String str){
- List<Character> list = new ArrayList<>();
- for (Character ch : str.toCharArray()) {
- list.add(ch);
- }
- return list.stream();
- }
其實(shí)map方法就相當(dāng)于Collaction的add方法,如果add的是個(gè)集合得話就會(huì)變成二維數(shù)組,而flatMap 的話就相當(dāng)于Collaction的addAll方法,參數(shù)如果是集合得話,只是將2個(gè)集合合并,而不是變成二維數(shù)組。
排序
關(guān)于排序相關(guān)的方法如下表所示。
從上述表格可以看出:sorted有兩種方法,一種是不傳任何參數(shù),叫自然排序,還有一種需要傳Comparator 接口參數(shù),叫做定制排序。
先來(lái)看Java8中Stream接口對(duì)于sorted()方法的聲明,如下所示。
- Stream<T> sorted();
- Stream<T> sorted(Comparator<? super T> comparator);
sorted()方法的定義比較簡(jiǎn)單,我就不再贅述了。
我們也可以按照如下方式來(lái)使用Stream的sorted()方法。
- / 自然排序
- List<Employee> persons = list.stream().sorted().collect(Collectors.toList());
- //定制排序
- List<Employee> persons1 = list.stream().sorted((e1, e2) -> {
- if (e1.getAge() == e2.getAge()) {
- return 0;
- } else if (e1.getAge() > e2.getAge()) {
- return 1;
- } else {
- return -1;
- }
- }).collect(Collectors.toList());
寫在最后
如果覺(jué)得文章對(duì)你有點(diǎn)幫助,請(qǐng)微信搜索并關(guān)注「 冰河技術(shù) 」微信公眾號(hào),跟冰河學(xué)習(xí)Java8新特性。
最后,附上Java8新特性核心知識(shí)圖,祝大家在學(xué)習(xí)Java8新特性時(shí)少走彎路。
本文轉(zhuǎn)載自微信公眾號(hào)「冰河技術(shù)」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系冰河技術(shù)公眾號(hào)。




































