Java基礎系列-Stream

原創做品,能夠轉載,可是請標註出處地址:http://www.javashuo.com/article/p-kplvntgn-mr.htmlhtml

1、概述

Stream操做簡稱流操做,這裏的流與IO流毫無關係,這裏的流指的是流式操做,就是流水線操做。java

Stream流操做主要包包括三大模塊:建立流操做、中間流操做、終結流操做。api

其中建立流主要是建立Stream對象。每一個Stream對象只能使用一次終結操做。數組

中間流操做指的是各類中間流操做方法,好比去重、過濾、排序等app

終結流操做指的結果操做,終結操做的目的是產生最終結果。dom

2、建立流

2.1 基於數組建立流

public class StreamTest {
    public static void createStream() {
        // 經過數組生成流
        int[] ints = {1,2,3,4,5,6};
        IntStream s1 = Arrays.stream(ints);
        Stream s2 = Stream.of("111","222","333");
        String[] ss = {"123","321","456","654"};
        Stream<String> s3 = Arrays.stream(ss);
    }
}

3.2 經過構建器生成流

public class StreamTest {
    public static void createStream() {
        // 經過構建器生成流
        Stream<Object> s4 = Stream.builder().add("123").add("321").add("444").add("@21").build();
    }
}

3.3 基於集合生成流

public class StreamTest {
    public static void createStream() {
        // 經過集合生成流
        List<String> lists = Arrays.asList("123","321","1212","32321");
        Stream<String> s5 = lists.stream();
        Stream<String> s6 = lists.parallelStream();// 並行流
    }
}

3.4 建立空流

public class StreamTest {
    public static void createStream() {
        // 建立空流
        Stream<String> s7  = Stream.empty();
    }
}

3.5 基於函數建立無限流

public class StreamTest {
    public static void createStream() {
        // 建立無限流
        Stream.generate(()->"number"+new Random().nextInt()).limit(100).forEach(System.out::println);
        Stream.iterate(0,n -> n+2).limit(10).forEach(System.out::println);
    }
}

3、流中間操做

這裏的流中間操做指的是該操做的返回值仍然是流。ide

序號 操做 方法 說明 備註
1 filter Stream filter(Predicate<? super T> predicate) 返回當前流中知足參數predicate過濾條件的元素組成的新流 過濾器
2 map Stream map(Function<? super T, ? extends R> mapper) 返回經過給定mapper做用於當前流的每一個元素以後的結果組成的新流 函數
3 mapToInt IntStream mapToInt(ToIntFunction<? super T> mapper) 返回經過給定mapper做用於當前流的每一個元素以後的結果組成的新的Int流 函數
4 mapToLong LongStream mapToLong(ToLongFunction<? super T> mapper) 返回經過給定mapper做用於當前流的每一個元素以後的結果組成的新的Long流 函數
5 mapToDouble DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) 返回經過給定mapper做用於當前流的每一個元素以後的結果組成的新的Double流 函數
6 flatMap Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) 根據給定的mapper做用於當前流的每一個元素,將結果組成新的流來返回 扁平函數
7 flatMapToInt IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) 根據給定的mapper做用於當前流的每一個元素,將結果組成新的Int流來返回 扁平函數
8 flatMapToLong LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) 根據給定的mapper做用於當前流的每一個元素,將結果組成新的Long流來返回 扁平函數
9 flatMapToDouble DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) 根據給定的mapper做用於當前流的每一個元素,將結果組成新的Double流來返回 扁平函數
10 distinct Stream distinct() 返回去掉當前流中重複元素以後的新流 去重
11 sorted Stream sorted() 返回當前流中元素排序以後的新流,須要元素類型實現Comparable 排序
12 sorted Stream sorted(Comparator<? super T> comparator) 返回當前流中元素排序以後的新流,須要傳遞一個Comparator 排序
13 peek Stream peek(Consumer<? super T> action) 針對流中的每一個元素執行操做action 查閱
14 limit Stream limit(long maxSize) 返回指定的數量的元素組成的新流 限制
15 skip Stream skip(long n) 返回第n個以後的元素組成的新流 跳過

扁平函數,就是將當前流的每一個元素經過執行給定的mapper操做,從而擴充,釋放每一個元素內的子元素,從而造成一個由全部子元素組成的新流,好比當前流是包含N個字符串的流,使用這個方法,能夠獲取到包含字符串中字符組成的流。函數

3.1 filter

filter方法是過濾器方法,針對的是流中全部元素,知足條件的元素將會被保留以組成新的流。工具

public class StreamTest {
    public static void filterTest(List<String> list){
        list.stream()
                .filter(e -> e.length() > 4 && e.length()<7)// 過濾掉長度小於等於4,大於等於7的元素
                .peek(System.out::println)// 查閱中間流結果
                .collect(Collectors.toList());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","asdaa","3e3e3e","2321eew","212121121");
        filterTest(list);
    }
}

執行結果爲:ui

asdaa
3e3e3e

filter方法的參數是Predicate類型,這個函數式接口用於獲取一個參數返回一個boolean值,整個參數做爲過濾條件。

3.2 map

map方法能夠理解爲函數,須要針對流中的每一個元素執行,而後將執行的結果組成新的流返回。

public class StreamTest {
    public static void mapTest(List<String> list){
        list.stream()
                .map(e -> "@" + e)// 爲每一個元素執行操做:添加前綴
                .peek(System.out::println)// 查閱中間流結果
                .collect(Collectors.toList());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","asdaa","3e3e3e","2321eew","212121121");
        mapTest(list);
    }
}

執行結果爲:

@123
@456
@789
@1101
@asdaa
@3e3e3e
@2321eew
@212121121

map方法的參數類型爲Function,該函數式接口用於接受一個參數,返回一個結果。

mapToInt、mapToLong、mapToDouble方法是map方法的擴展,其參數分別爲ToIntFunction、ToLongFunction、ToDoubleFunction,分別接受一個參數,返回指定類型的值,分別爲int、long、double,那麼定義方法的時候就要注意返回值的類型了,必須一致,最後組成的新流就是一個int或long或double元素流(IntStream、LongStream、DoubleStream)。

mapToInt的簡單使用(其餘相似):

public class StreamTest {
    public static void mapToIntTest(List<String> list){
        list.stream()
                .mapToInt(e -> e.length())// 以元素的長度爲新流
                .peek(System.out::println)// 查詢中間結果
                .toArray();
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","asdaa","3e3e3e","2321eew","212121121");
        mapToIntTest(list);
    }
}

執行結果爲:

3
3
3
4
5
6
7
9

3.3 flatMap

flatMap和map仍是有點關係的,都是針對流中的每個元素進行操做,將結果組成新流,不過flatMap含有一層擴展之意,就是當流中元素包含子元素的時候,經過該方法,獲取到元素的子元素,並將子元素組成新流返回。

public class StreamTest {
    public static void flatMap(List<String> list){
        list.stream()
                .filter(e -> e.length()>5 && e.length()<7)
                .peek(System.out::println)
                .map(e -> e.split(""))// 將每一個字符串元素分解爲字符數組
                .flatMap(Arrays::stream)//將每一個字符數組並轉化爲流
                .peek(System.out::println)
                .collect(Collectors.toList());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","asdaa","3e3e3e","2321eew","212121121");
        flatMap(list);
    }
}

執行結果爲:

3e3e3e
3
e
3
e
3
e

flatMapToInt、flatMapToLong、flatMapToDouble相似於以前的mapToInt之類。

3.4 distinct

distinct方法用於去重,很簡單。

public class StreamTest {
    public static void distinctTest(){
        int[] int1 = {1,2,3,4};
        int[] int2 = {5,3,7,1};
        List<int[]> ints = Arrays.asList(int1,int2);
        ints.stream()
                .flatMapToInt(Arrays::stream)
                .distinct()
                .peek(System.out::println)
                .toArray();
    }
    public static void main(String[] args) {
        distinctTest();
    }
}

執行結果爲:

1
2
3
4
5
7

結果中顯而易見,重複的1和3被去除了。

3.5 sorted

sorted表示對流中的元素進行排序,須要使用Conparable和Comparator。

public class StreamTest {
    public static void sortedTest(List<String> list){
        System.out.println("----天然順序:");
        list.stream().sorted().peek(System.out::println).collect(Collectors.toList());
        System.out.println("----指定排序:");
        list.stream().sorted((a,b) -> a.length()-b.length()).peek(System.out::println).collect(Collectors.toList());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","asdaa","3e3e3e","2321eew","212121121");
        sortedTest(list);
    }
}

執行結果爲:

----天然順序:
1101
123
212121121
2321eew
3e3e3e
456
789
asdaa
----指定排序:
123
456
789
1101
asdaa
3e3e3e
2321eew
212121121

當調用無參的sorted方法時,採用天然排序法排序,當使用指定比較器的方式時,能夠自由指定排序規則。

3.6 limit

limit可用於從首個元素開始截取N個元素,組成新流返回。

public class StreamTest {
    public static void limitTest(List<String> list){
        list.stream().limit(2).peek(System.out::println).collect(Collectors.toList());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","asdaa","3e3e3e","2321eew","212121121");
        limitTest(list);
    }
}

執行結果爲:

123
456

3.7 skip

skip表示放棄N個元素,將剩餘元素組成新流返回。

public class StreamTest {
    public static void skipTest(List<String> list){
        list.stream().skip(2).peek(System.out::println).collect(Collectors.toList());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","asdaa","3e3e3e","2321eew","212121121");
        skipTest(list);
    }
}

執行結果爲:

789
1101
asdaa
3e3e3e
2321eew
212121121

放棄了前2個元素,將剩餘元素組成了新流。

4、流終結操做

序號 操做 方法 說明 備註
1 forEach void forEach(Consumer<? super T> action) 對流中的每一個元素執行指定的操做action 遍歷
2 forEachOrdered void forEachOrdered(Consumer<? super T> action) 若是有序,則按序遍歷流中元素,針對每一個元素執行指定操做 按序遍歷
3 toArray Object[] toArray() 返回一個包含流中全部元素的數組 數組化
4 toArray A[] toArray(IntFunction<A[]> generator) 返回一個包含流中全部元素的參數指定類型的數組 數組化
5 reduce T reduce(T identity, BinaryOperator accumulator) 以給定初始值爲基礎概括流中元素,返回一個值 概括
6 reduce Optional reduce(BinaryOperator accumulator) 直接概括流中的元素,返回一個封裝有結果的Optional 概括
7 reduce <U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner) 以給定的初始值爲基礎,(並行)概括流中元素,最後將各個線程的結果再統一概括,返回一個值 概括
8 collect <R, A> R collect(Collector<? super T, A, R> collector) 根據給定的收集器收集元素 概括
9 collect R collect(Supplier supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner) 根據給定的各個參數概括元素 概括
10 max Optional max(Comparator<? super T> comparator) 根據給定的比較器,返回流中最大元素的Optional表示 最大值
11 min Optional min(Comparator<? super T> comparator) 根據給定的比較器,返回流中最小元素的Optional表示 最小值
12 count long count() 返回流中元素的個數 計數
13 anyMatch boolean anyMatch(Predicate<? super T> predicate) 校驗流中是否有知足給定條件的元素 校驗
14 allMatch boolean allMatch(Predicate<? super T> predicate) 校驗流中的元素是否所有知足給定條件 校驗
15 noneMatch boolean noneMatch(Predicate<? super T> predicate) 校驗流中的元素是否全不知足給點條件 校驗
16 findFirst Optional findFirst() 返回首個元素的Optional表示,若是爲空流,返回空的Optional 返回首個元素
17 findAny Optional findAny() 若是流中有元素,則返回第一個元素的Optional表示,不然返回一個空的Optional 校驗是否爲空流

4.1 forEach和forEachOrdered

forEach就是遍歷操做,針對流中的每一個元素作最後的操做。

public class StreamTest {
    public static void forEachTest(List<String> list){
        list.stream().parallel().forEach(System.out::println);
    }
    public static void forEachOrderedTest(List<String> list){
        list.stream().parallel().forEachOrdered(System.out::println);
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        forEachTest(list);
        System.out.println("----------");
        forEachOrderedTest(list);
    }
}

執行結果爲:

asdaa
212121121
789
1101
2321eew
3e3e3e
456
123
----------
123
456
789
1101
212121121
asdaa
3e3e3e
2321eew

兩者都是遍歷操做,從結果是能夠看出來,若是是單線程(也就是不加parallel方法的狀況)那麼兩者結果是一致的,可是若是採用並行遍歷,那麼就有區別了,forEach並行遍歷不保證順序(順序隨機),forEachOrdered倒是保證順序來進行遍歷的。

4.2 toArray

public class StreamTest {
    public static void toArrayTest(List<String> list){
        Object[] objs = list.stream().filter(e -> e.length()>6).toArray();
        String[] ss = list.stream().filter(e -> e.length()>6).toArray(String[]::new);
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        toArrayTest(list);
    }
}

toArray有兩個方法,一個是無參方法,一個有參方法。

無參方法返回的只能是Object[]數組類型,而有參方法,能夠指定結果數組類型,此乃兩者區別。

使用有參方法能夠直接完成類型轉換,一次到位。

4.4 reduce

reduce方法有三個重載的方法,

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    Optional<T> reduce(BinaryOperator<T> accumulator);// 編號1
    T reduce(T identity, BinaryOperator<T> accumulator);// 編號2
    <U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);// 編號3
}

這三個方法的做用實際上是同樣的,就是概括總結的意思。

首先看編號1方法,只有一個參數accumulator,這是一個累加器,方法的做用就是將這個累加器做用到流中的每個元素,他須要兩個輸入參數,有一個輸出參數,意思是對兩個元素執行某些操做,返回一個結果,而後將這個結果與下一個元素做爲參數再輸入該方法,執行操做後再返回一個新結果,以此類推,直到最後一個元素執行完畢,返回的就是最終結果,由於流中的元素咱們是不肯定的,那麼咱們就沒法肯定reduce的結果,由於若是流爲空,那麼將會返回null,因此使用Optional做爲返回值,妥善處理null值。

再看編號2方法,在編號1方法的基礎上加了一個identity,且再也不使用Optional,爲何呢,由於新加的identity實際上是個初始值,後續的操做都在這個值基礎上執行,那麼也就是說,,若是流中沒有元素的話,還有初始值做爲結果返回,不會存在null的狀況,也就不用Optional了。

再看編號3方法,在編號2方法的基礎上又加了一個參數combiner,其實這個方法是用於處理並行流的概括操做,最後的參數combiner用於概括各個並行的結果,用於得出最終結果。

那麼若是不使用並行流,通常使用編號2方法就足夠了。

示例:

public class StreamTest {
    public static void reduceTest(){
        List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9);
        Optional<Integer> optional = ints.stream().reduce(Integer::sum);
        System.out.println(optional.get());
        System.out.println("-------------");
        Integer max = ints.stream().reduce(Integer.MIN_VALUE, Integer::max);
        System.out.println(max);
        System.out.println("-------------");
        Integer min = ints.parallelStream().reduce(Integer.MAX_VALUE, Integer::min, Integer::min);
        System.out.println(min);
    }
    public static void main(String[] args) {
        reduceTest();
    }
}

執行結果爲:

45
-------------
9
-------------
1

4.5 collect

collect操做是Stream中最強大的方法了,幾乎能夠獲得任何你想要的結果,collect方法有兩個重載方法:

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);// 編號1
    <R, A> R collect(Collector<? super T, A, R> collector);// 編號2
}

collect是收集的意思,這裏的做用就是收集概括,將流中的數據映射爲各類結果。

首先看看編號1方法,有三個參數:supplier用於生成一個R類型的結果容器來盛放結果,accumulator累加器用於定義盛放的方式,其中T爲一個元素,R爲結果容器,第三個參數combiner的做用是將並行操做的各個結果整合起來。

public class StreamTest {
    public static void collectTest1(List<String> list){
        ArrayList<String> arrayList = list.stream().skip(4).collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
        arrayList.forEach(System.out::println);
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        collectTest1(list);
    }
}

執行結果:

212121121
asdaa
3e3e3e
2321eew

例子中,第一個:ArrayList::new表示建立一個新的ArrayList集合,第二個 ArrayList::add表示將元素一個一個添加到以前的集合中,第三個ArrayList::addAll表示將多個線程的ArrayList集合一個一個的總體添加到第一個集合中,最終整合出一個最終結果並返回。

而後咱們重點來看看編號2方法。

它只須要一個Collector類型的參數,這個Collector能夠稱呼爲收集器,咱們能夠隨意組裝一個收集器來進行元素概括。

Collector是定義來承載一個收集器,可是JDK提供了一個Collectors工具類,在這個工具類裏面預實現了N多的Collector供咱們直接使用,以前的Collectors.toList()就是其用法之一。具體見下文。

public class StreamTest {
    public static void collectTest2(List<String> list){
        Set<String> set = list.stream().skip(4).collect(Collectors.toSet());
        set.forEach(System.out::println);
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        collectTest2(list);
    }
}

執行結果爲:

212121121
2321eew
3e3e3e
asdaa

4.6 max\min

經過給定的比較器,得出流中最大\最小的元素,爲避免null返回,這裏使用Optional來封裝返回值。

public class StreamTest {
    public static void maxMinTest(List<String> list){
        System.out.println("長度最大:" + list.stream().max((a,b)-> a.length()-b.length()));
        System.out.println("長度最小:" + list.stream().min((a,b)-> a.length()-b.length()));
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        maxMinTest(list);
    }
}

執行結果爲:

長度最大:Optional[212121121]
長度最小:Optional[123]

4.7 count

count是無參方法,用於計數,返回流中元素個數。

public class StreamTest {
    public static void countTest(List<String> list){
        System.out.println("元素個數爲:" + list.stream().count());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        countTest(list);
    }
}

執行結果爲:

元素個數爲:8

4.8 anyMatch

該方法須要一個Predicate參數,用於校驗流中的元素,只要有一個知足規則,則返回true,全不知足,返回false。

public class StreamTest {
    public static void anyMatchTest(List<String> list){
        System.out.println(list.stream().anyMatch(e -> e.length()>10));
        System.out.println(list.stream().anyMatch(e -> e.length()>8));
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        anyMatchTest(list);
    }
}

執行結果爲:

false
true

4.9 allMatch

該方法一樣須要一個Predicate參數,用於校驗流中的全部元素,只有所有知足規則才能返回true,只要有一個不知足則返回false。

public class StreamTest {
    public static void allMatchTest(List<String> list){
        System.out.println(list.stream().allMatch(e -> e.length()>1));
        System.out.println(list.stream().allMatch(e -> e.length()>3));
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        allMatchTest(list);
    }
}

執行結果爲:

true
false

4.10 noneMatch

該方法一樣須要一個Predicate參數,用於校驗流中的全部元素,只有全部元素都不知足規則的狀況下返回true,不然返回false。

public class StreamTest {
    public static void noneMatchTest(List<String> list){
        System.out.println(list.stream().noneMatch(e -> e.length()>10));
        System.out.println(list.stream().noneMatch(e -> e.length()>8));
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        noneMatchTest(list);
    }
}

執行結果爲:

true
false

4.11 findFirst

該方法無參數,主要用於獲取流中的第一個元素,若是流無序,那麼可能返回任意一個。

public class StreamTest {
    public static void findFirstTest(List<String> list){
        System.out.println(list.stream().parallel().findFirst().get());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        findFirstTest(list);
    }
}

執行結果爲:

123

4.12 findAny

該方法無參數,主要用於獲取流中的任一元素。

public class StreamTest {
    public static void findAnyTest(List<String> list){
        System.out.println(list.stream().parallel().findAny().get());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        findAnyTest(list);
    }
}

執行結果爲:

asdaa

5、總結

流式操做代碼描述性強,易理解,並且功能強大,能夠簡化不少集合操做。在咱們須要對集合數據進行處理的時候,不妨試試使用流式操做來實現。

參考:

相關文章
相關標籤/搜索