在 .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
|
對流中的數據進行過濾篩選 |
Stream
|
獲取流中前幾個元素 |
Stream
|
跳過流中前幾個元素 |
static
|
將 a 流和 b 流合並成一個流 |
Stream
|
獲取流中的不同的元素(根據 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
|
把元素收集到List集合中 |
public static
|
把元素收集到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 文檔即可,總之使用起來都很簡單。希望本篇博客能夠對大家有所幫助。