Java8中Stream的用法

        Stream 是用函數式編程方式在集合類上進行復雜操作的工具,其集成了Java 8中的衆多新特性之一的聚合操作,開發者可以更容易地使用Lambda表達式,並且更方便地實現對集合的查找、遍歷、過濾以及常見計算等。

Stream的操作分類

Stream的操作有Intermediate、Terminal和Short-circuiting:

  • Intermediate:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered、concat,limit

  • Terminal:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator

  • Short-circuiting:
    anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

Intermediate

        Intermediate主要是用來對Stream做出相應轉換及限制流,其是一箇中間操作,會使用惰性求值,不會說一個Intermediate操作就會掃描流中所有的數據,而是在有Terminal時,會將前面的Intermediate收集起來,然後一起進行數據的分析。

        使用Intermediate實際上是將源Stream轉換爲一個新的Stream,在新生成的Stream中仍然可以繼續進行Intermediate的操作,但是對於Terminal來說只能有一個。

map

        map方法將對於Stream中包含的元素使用給定的轉換函數進行轉換操作,新生成的Stream只包含轉換生成的元素。爲了提高處理效率,官方已封裝好了,三種變形:mapToDouble,mapToInt,mapToLong。如果我們知道要將數據轉化爲int,long和double時儘量使用這三種方法,不要讓虛擬機去自己推斷,畢竟咱們的心思不告訴它,它會花費時間去思考的,這個就會有一定的效率問題。mapToDouble,mapToInt,mapToLong,這三種變形轉換之後是基本類型,double,int和long,而不是包裝類型。

        點開Stream的map方法,如下:

    /**
     * Returns a stream consisting of the results of applying the given
     * function to the elements of this stream.
     *
     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
     * operation</a>.
     *
     * @param <R> The element type of the new stream
     * @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
     *               <a href="package-summary.html#Statelessness">stateless</a>
     *               function to apply to each element
     * @return the new stream
     */
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

          知道了其是使用Function函數式接口來進行的,Function接口有一個apply方法,傳入一個數,經過操作之後,返回一個值,不懂得自行百度。

          所以我們明白了map的用法,接收一個值(這個值就是流中值),經過一系列的操作,返回我們需要的值。

          現在提一個簡單的需求,將字符串轉換爲數字並打印出來,使用Stream的map的寫法:

public class Convert{
    public static void main(String[] args) {
        Stream.of("12","13","1","34","34","67").map(Integer::parseInt)//這裏可以使用mapToInt
                .forEach(System.out::println);  //forEach爲將轉換好的流進行打印
    }
}

filter

        filter方法對原Stream按照指定條件過濾,在新建的Stream中,只包含滿足條件的元素,將不滿足條件的元素過濾掉。

    /**
     * Returns a stream consisting of the elements of this stream that match
     * the given predicate.
     *
     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
     * operation</a>.
     *
     * @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
     *                  <a href="package-summary.html#Statelessness">stateless</a>
     *                  predicate to apply to each element to determine if it
     *                  should be included
     * @return the new stream
     */
    Stream<T> filter(Predicate<? super T> predicate);

        從上面的代碼可以知道,filter使用需要傳遞Predicate函數式接口,這個接口也是JDK8提供的最用函數式接口,這個接口需要傳入一個數,然後返回一個布爾值,filter函數也是根據這個返回值來進行過濾的,如果值爲false則過濾掉。

       現在提一個簡單的需求,將字符串轉換爲數字,並且只保留偶數的,最後打印出來,使用Stream的filter的寫法:

public class Test02 {
    public static void main(String[] args) {
        Stream.of("12", "13", "1", "34", "34", "67").mapToInt(x -> Integer.parseInt(x))//這個map的寫法和那個一樣,只不過上面那種更加簡潔
                .filter(x -> x % 2 == 0)//filter判斷的值要是布爾類型的
                .forEach(x -> System.out.println(x));
    }
}

distinct

     distinct方法以達到去除掉原Stream中重複的元素,生成的新Stream中沒有沒有重複的元素。distinct不用傳值參數,去除的重複元素是除第一個以外的所有重複的數,也就是說一個數組中有很多個4,那麼除了保留第一個4外,其他的均過濾掉,例如:有一串數字:10,3,5,6,8,10,5,8,1,10,去重之後的排列是10,3,5,6,8,1,10

    /**
     * Returns a stream consisting of the distinct elements of this stream.
     *
     * <p>This is a <a href="package-summary.html#StreamOps">stateful
     * intermediate operation</a>.
     *
     * @return the new stream
     */
    IntStream distinct();

    現在提一個簡單的需求,將字符串轉換爲數字,去重之後保留偶數,最後打印出來,使用Stream的filter的寫法:

public class DistinctTest {
    public static void main(String[] args) {
        Stream.of("12", "13", "1", "34", "34", "67").mapToInt(x -> Integer.parseInt(x))
                .distinct()
                .filter(x -> x % 2 == 0)
                .forEach(x -> System.out.println(x));
    }
}

sorted

       sorted方法將對原Stream進行排序,返回一個有序列的新Stream。sorterd有兩種變體sorted(),sorted(Comparator),前者將默認使用Object.equals(Object)進行排序,而後者接受一個自定義排序規則函數(Comparator),可按照意願排序。

public class SortTest {
    public static void main(String[] args) {
        Stream.of("12", "13", "1", "34", "34", "67").map(x -> Integer.parseInt(x))
                .distinct()
                .sorted(((x, y) -> y - x))
                .forEach(x -> System.out.println(x));
    }
}

peek

        peek方法生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費函數(Consumer實例),新Stream每個元素被消費的時候都會執行給定的消費函數,並且消費函數優先執行。

        在Terminal操作執行時,Intermediate的操作纔會執行,對於peek來說,如果peek的操作無法與peek之後關於流的操作無法進行優化一次執行,那麼關於peek的操作就會先執行(不會說一個Intermediate操作就會掃描流中所有的數據,而是在有Terminal時,會將前面的Intermediate收集起來,然後一起進行數據的分析),例如:

public class PeekTest01 {
    public static void main(String[] args) {
        Stream.of("12", "13", "1", "34", "34", "67").map(x -> Integer.parseInt(x))
                .peek(x->System.out.println("輸出的結果爲"+x))
                .distinct()
                .sorted(((x, y) -> y - x))
                .forEach(System.out::println);
    }
}
//輸出結果爲
//輸出的結果爲12
//輸出的結果爲13
//輸出的結果爲1
//輸出的結果爲34
//輸出的結果爲34
//輸出的結果爲67
//67
//34
//13
//12
//1

        示例2:

public class PeekTest02 {
    public static void main(String[] args) {
        Stream.of("12", "13", "1", "34", "34", "67").map(x -> Integer.parseInt(x))
                .sorted(((x, y) -> y - x))
                .peek(x->System.out.println("輸出的結果爲"+x))
                .distinct()
                .forEach(System.out::println);
    }
}
//輸出的結果爲67
//67
//輸出的結果爲34
//34
//輸出的結果爲34
//輸出的結果爲13
//13
//輸出的結果爲12
//12
//輸出的結果爲1
//1

skip

         skip方法接受一個long類型的數值,將過濾掉原Stream中的前N個元素,返回剩下的元素所組成的新Stream。如果原Stream的元素個數大於N,將返回原Stream的後(原Stream長度-N)個元素所組成的新Stream;如果原Stream的元素個數小於或等於N,將返回一個空Stream。

public class SkipTest {
    public static void main(String[] args) {
        Stream.of("12", "13", "1", "34", "34", "67","10","45").map(x -> Integer.parseInt(x))
                .skip(3)
                .forEach(System.out::println);
    }
}
//輸出結果
//34
//34
//67
//10
//45

concat

        concat方法將兩個Stream連接在一起,合成一個Stream。若兩個輸入的Stream都時排序的,則新Stream也是排序的;若輸入的Stream中任何一個是並行的,則新的Stream也是並行的;若關閉新的Stream時,原兩個輸入的Stream都將執行關閉處理。

public class ConcatTest03 {
    public static void main(String[] args) {
        Stream.concat(Stream.of(1, 2, 4), Stream.of(3, 5))
                .forEach(integer -> System.out.print(integer + "  "));
    }
}
// 打印結果
// 1  2  4  3  5

limit

      limit接收一個long類型的數組,返回一個限定大小的流,如果limit(n),只會選取前n個值,之後的值則會被拋棄。

public class LimitTest {
    public static void main(String[] args) {
        Stream.of("12", "13", "1", "34", "34", "67","10","45").map(x -> Integer.parseInt(x))
                .limit(4)
                .forEach(x-> System.out.print(x+"  "));
    }
}
//輸出結果
//12  13  1  34 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章