Java 集合通過 Stream 流過濾提取


在 .NET 的 C# 語言中,對集合過濾提取的技術是 Linq,其鏈式編程風格簡便易讀,深受大家喜愛。那么 Java 是否也提供了類似的技術?答案肯定的,那就是 Java 使用 Stream 流對集合元素進行過濾提取,只不過其功能沒有 C# 中的 Linq 那么豐富。但不管怎么說,這已經算是很不錯了,畢竟省去了頻繁使用循環遍歷過濾提取集合元素的繁瑣步驟。

下面我們就針對每種集合類型,演示一下 Stream 流過濾集合元素數據的技術吧。


一、生成 Stream 流

對於 List 和 Set 這種 Collection 類型的集合,只需要調用其 Stream() 方法,即可生成 Stream 流。
List 接口的常用實現類為 ArrayList 和 LinkedList 。Set 接口的常用實現類為 HashSet 和 TreeSet 。

對於 Map 類型的集合,需要先將 Map 轉換為 Collection 類型的集合,間接生成 Stream 流。
常用的 Map 類有 HashMap 和 TreeMap。

對於數組來說,需要通過 Arrays 類中的靜態方法 Stream 來生成 Stream 流。

也可以通過 Stream 的靜態方法 of 來將一些相同類型的臨時數據組成一個集合,生成 Stream 流。

具體代碼如下所示:

import java.util.*;
import java.util.stream.Stream;

//不同集合生成 Stream 流
public class StreamTest {
    public static void main(String[] args) {
        //Collection 類型的集合可以使用 stream() 生成流
        List<String> list = new ArrayList<String>();
        Stream<String> listStream = list.stream();

        Set<String> set = new HashSet<String>();
        Stream<String> setStream = set.stream();

        //Map 類型的集合可以先轉換為 Collection 類型的集合,間接生成流
        Map<String, Integer> map = new HashMap<String, Integer>();
        //對於 Map 的鍵集合,先轉換為 Set 集合,然后生成流
        Stream<String> keyStream = map.keySet().stream();
        //對於 Map 的值集合,先轉換為 Collection 集合,然后生成流
        Stream<Integer> valueStream = map.values().stream();
        //對於 Map 的鍵值對集合,先轉換為 Set 集合,然后生成流
        Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

        //數組可以通過 Arrays 中的靜態方法 stream 生成流
        String[] strArray = {"jobs", "alpha", "monkey", "wolfer"};
        Stream<String> strArrayStream = Arrays.stream(strArray);

        //可以通過 Stream 接口的靜態方法 of(T... values) 將相同類型的一組數據生成流
        Stream<String> strArrayStream2 = Stream.of("winston", "reid", "celio");
        Stream<Integer> intStream = Stream.of(11, 22, 33, 44);
    }
}

二、Stream 流的常用方法

Stream 流的常用方法如下:

方法名 說明
Stream filter(Predicate predicate) 對流中的數據進行過濾篩選
Stream limit(long maxSize) 獲取流中前幾個元素
Stream skip(long n) 跳過流中前幾個元素
static Stream concat(Stream a, Stream b) 將 a 流和 b 流合並成一個流
Stream distinct() 獲取流中的不同的元素(根據 Object.equals(Object) 判斷元素是否相同)
void forEach(Consumer action) 對此流的每個元素進行遍歷
long count() 獲取流中的元素個數

我們最經常使用的是 filter 方法,其代碼演示如下:
import java.util.ArrayList;

public class StreamTest {
    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<String>();
        list.add("任肥肥");
        list.add("候胖胖");
        list.add("任我行");
        list.add("藺贊贊");
        list.add("任政富");
        list.add("喬豆豆");

        //要求把【以任開頭的字符串】打印出來
        list.stream().filter(s->s.startsWith("任")).forEach(System.out::println);

        System.out.println("---------");

        //要求獲取【以任開頭的字符串】的數量
        long count = list.stream().filter(s -> s.startsWith("任")).count();
        System.out.println(count);
    }
}

/*
打印的結果如下所示:
任肥肥
任我行
任政富
---------
3
*/

limit 方法和 skip 方法的代碼演示如下:
import java.util.ArrayList;

public class StreamTest {
    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<String>();
        list.add("任肥肥");
        list.add("候胖胖");
        list.add("任我行");
        list.add("藺贊贊");
        list.add("任政富");
        list.add("喬豆豆");

        //只打印前 3 個
        list.stream().limit(3).forEach(System.out::println);
        System.out.println("---------");

        //跳過前 3 個,打印剩余的
        list.stream().skip(3).forEach(System.out::println);
        System.out.println("---------");

        //跳過前 2 個,再打印剩余元素的前 2 個
        list.stream().skip(2).limit(2).forEach(System.out::println);
    }
}

/*
打印的結果如下所示:
任肥肥
候胖胖
任我行
---------
藺贊贊
任政富
喬豆豆
---------
任我行
藺贊贊
*/

concat 方法和 distinct 方法的代碼演示如下:
import java.util.ArrayList;
import java.util.stream.Stream;

public class StreamTest {
    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<String>();
        list.add("任肥肥");
        list.add("候胖胖");
        list.add("任我行");
        list.add("藺贊贊");
        list.add("任政富");
        list.add("喬豆豆");

        //提取前 3 個元素,生成一個流
        Stream<String> s1 = list.stream().limit(3);

        //跳過前 1 個元素,使用后面 3 個元素生成一個流
        Stream<String> s2 = list.stream().skip(1).limit(3);

        //將兩個流合並,然后打印出合並后的流的元素
        Stream<String> s3 = Stream.concat(s1, s2);
        s3.forEach(System.out::println);

        System.out.println("---------");

        /*
        上面的 s1, s2, s3 這三個流,
        一旦遇到 forEach 或 count 方法,執行完后,流就全部釋放了,不能再用。
        因此下面再進行流的過濾時,即使是相同的元素的流,也需要重新生成流
        */

        //將兩個流合並,去重后,進行打印
        Stream.concat(list.stream().limit(3), list.stream().skip(1).limit(3))
                .distinct().forEach(System.out::println);
    }
}

/*
打印的結果如下所示:
任肥肥
候胖胖
任我行
候胖胖
任我行
藺贊贊
---------
任肥肥
候胖胖
任我行
藺贊贊
*/

三、Stream 流中數據的收集

上面的代碼示例,只是通過 Stream 流把集合數據進行過濾,一旦在最后執行了 forEach 或 count 等方法后,Stream 流就會釋放銷毀,后續就無法使用了。此時我們肯定想把 Stream 流中的數據提取出來,放到集合中。這就需要用到以下相關的方法:

方法名 說明
R collect(Collector collector) 把結果收集到集合中

Collectors 工具類,提供了具體的收集方式:

方法名 說明
public static Collector toList() 把元素收集到List集合中
public static Collector toSet() 把元素收集到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合中

具體代碼示例如下所示:
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamTest {
    public static void main(String[] args) {

        ArrayList<String> personlist = new ArrayList<String>();
        personlist.add("任肥肥,38歲");
        personlist.add("候胖胖,40歲");
        personlist.add("任我行,55歲");
        personlist.add("藺贊贊,34歲");
        personlist.add("任政富,58歲");
        personlist.add("喬豆豆,33歲");

        //把【以任開頭的字符串】提取出來,存儲到新的 List 集合中,並打印出來
        Stream<String> listStream1 = personlist.stream().filter(s -> s.startsWith("任"));
        List<String> list = listStream1.collect(Collectors.toList());
        for (String p : list) {
            System.out.println(p);
        }

        System.out.println("---------");

        //把【以任開頭的字符串】提取出來,提取出【姓名】,存儲在 Set 集合中,並打印出來
        Stream<String> listStream2 = personlist.stream()
                .filter(s -> s.startsWith("任")).map(s->s.split(",")[0]);
        Set<String> set = listStream2.collect(Collectors.toSet());
        for(String name : set) {
            System.out.println(name);
        }

        System.out.println("---------");

        //把【以任開頭的字符串】提取出來,提取出【姓名】和【年齡】,存放到 Map 集合中
        //在 Map 集合中,姓名為鍵,年齡為值。然后打印出來
        Stream<String> listStream3 = personlist.stream().filter(s -> s.startsWith("任"));
        Map<String, String> map =
                listStream3.collect(
                        Collectors.toMap(s -> s.split(",")[0], s -> s.split(",")[1]));
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            System.out.println(key + " <---> " + map.get(key));
        }
    }
}

/*
打印的結果如下所示:
任肥肥,38歲
任我行,55歲
任政富,58歲
---------
任我行
任肥肥
任政富
---------
任我行 <----> 55歲
任肥肥 <----> 38歲
任政富 <----> 58歲
*/

當然有關 Stream 流操作集合元素的方法還有很多,比如 max() 方法,min() 方法等等,這里就不介紹了,大家在工作中需要用到的時候,查詢以下 JDK 文檔即可,總之使用起來都很簡單。希望本篇博客能夠對大家有所幫助。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM