一起來學Java8(七)——Stream(上)
從Java8開始,新增了一個java.util.stream
包,這個包下的類和介面用來處理集合中的元素,在這個包下面有一個Stream介面,我們主要使用這個介面來對集合進行操作。
建立Stream
首先來看下建立Stream有哪幾種方式。
使用Stream自帶的靜態方法生成Stream物件,常見的靜態方法有以下幾個:
- Stream.of(T)
- Stream.of(T... values)
- Stream.generate(Supplier)
- Stream.iterate(T,UnaryOperator)
- Stream.empty()
現在來看下每個靜態方法的作用
Stream.of(T) & Stream.of(T... values)
Stream.of是由兩個過載方法組成,一個傳入單值,一個傳入陣列
String[] arr = {"hello","world"};
Stream streamArr = Stream.of(arr);
String str = "hello world";
Stream streamSingle = Stream.of(str);複製程式碼
Stream.generate & Stream.iterate
Stream.generate和Stream.iterate可以用來生成具有多個元素的Stream,如果不加控制會一直生成下去,一般配合limit(n)使用
先來看下Stream.iterate
Stream<Integer> stream5 = Stream.iterate(0,n-> n+1)
.limit(5);
stream5.forEach(System.out::println);複製程式碼
列印
0
1
2
3
4複製程式碼
Stream.iterate方法第一引數設定一個初始值,第二個引數表示基於這個初始值,每次迴圈後返回一個新的值替換這個初始值。limit(5)表示迴圈5次結束,最後Stream中包含了5個元素。
再來看下Stream.generate
Stream.generate方法只有一個Supplier引數,意思是每次迴圈執行Supplier介面方法返回一個新的值,放入到Stream中,由於Supplier是一個函式式介面,因此可以直接寫成Lambda表示式
AtomicInteger i = new AtomicInteger();
Stream.generate(()-> {
return i.getAndIncrement();
})
.limit(5)
.forEach(System.out::println);複製程式碼
上面的程式碼同樣列印0~4。
除了Stream靜態方法之外,還可以使用Collection介面中的stream()方法來生成Stream物件。
Collection<String> list = Arrays.asList("hello","world");
Stream streamList = list.stream();複製程式碼
同理,只要是Collection介面的子類或實現類都可以使用stream()方法。
操作Stream
Stream中的方法有很多,大致歸納如下表格所示:
方法 | 方法引數 | 返回型別 | 描述 |
---|---|---|---|
filer | Predicate |
Stream |
過濾資料 |
distinct | 無 | Stream |
去重 |
map | Function |
Stream |
返回新的資料 |
flatMap | Function |
Stream |
返回新的資料,並做扁平化處理 |
sort | Comparator |
Stream |
對資料進行排序操作 |
limit | long | Stream |
擷取前幾條資料 |
skip | long | Stream |
跳過幾條資料 |
anyMatch | Predicate |
boolean | 匹配任意一條資料,如果匹配到返回true |
noneMatch | Predicate |
boolean | 如果沒有匹配到資料,返回true |
allMatch | Predicate |
boolean | 如果所有資料全部匹配到,返回true |
findAny | 無 | Optional |
返回任意一條資料 |
findFirst | 無 | Optional |
返回第一條資料 |
count | 無 | long | 返回元素個數 |
forEach | Consumer |
void | 遍歷元素,執行Consumer |
collect | Collector |
R | 元素收集 |
reduce | BinaryOperator |
Optional |
資料彙總 |
從方法的返回結果可以看出,這些方法可以分為兩大類,一類是返回Stream物件,可以繼續對Stream操作,這類方法也被稱之為中間操作(Intermediate operations)
,另一類是返回非Stream,結束操作,這類方法也被稱之為中端操作(Terminal operations)
,這兩類方法往往一起配合操作。
下面我們挑選其中的幾個方法來演示它們的作用。
filter
filter方法用來篩選出我們想要的資料,方法引數是一個Predicate介面,因為Predicate是一個函式式介面,我們可以使用Lambda表示式來寫。
Integer[] arr = { 1,2,3,4,5 };
long count = Stream.of(arr)
.filter(i -> i % 2 == 0)
.count();
System.out.println("偶數數量:" + count);複製程式碼
在這個例子中,我們篩選出了偶數數字,並且統計出偶數的數量。如果要列印每個偶數,可以使用forEach方法
Stream.of(arr)
.filter(i -> i % 2 == 0)
.forEach(System.out::println);複製程式碼
列印:
2
4複製程式碼
如果要查詢任意一個元素,可以使用findAny
int num = Stream.of(arr)
.filter(i -> i % 2 == 0)
.findAny()
.orElse(0);
System.out.println("findAny:" + num);複製程式碼
注意,findAny()返回的是一個Optional物件,因為有可能沒有找到資料,因此需要開發者自己處理沒有找到資料的情況。同理findFirst
也是返回一個Optional物件。
distinct
distinct方法會對元素進行去重操作,類似於SQL中的SELECT distinct xx
Stream.of(1,1,4)
.distinct()
.forEach(System.out::println)複製程式碼
列印
1
2
3
4複製程式碼
sorted
使用sorted方法可以對元素進行排序操作
Stream.of(6,7,8,5)
.sorted()
.forEach(System.out::println);複製程式碼
列印
1
2
5
6
7
8複製程式碼
sorted()預設是從小到大排列,如果要從大到小降序,可以使用.sorted(Comparator.reverseOrder())
Stream.of(6,5)
.sorted(Comparator.reverseOrder())
.forEach(System.out::println);複製程式碼
可以看到,sorted方法允許傳入一個比較器Comparator
讓開發者自己實現排序邏輯。下面是一個自定義Comparator例子:
@Data
@AllArgsConstructor
static class Goods {
private String goodsName;
private int price;
}
Stream.of(
new Goods("iphoneX",4000),new Goods("mate30 pro",5999),new Goods("redmek20",2999)
)
.sorted((goods1,goods2) -> {
return Integer.compare(goods1.getPrice(),goods2.getPrice());
})
.forEach(System.out::println);複製程式碼
這個列子演示了按商品價格從低到高排序。此處的sorted部分可以簡化為:.sorted(Comparator.comparing(Goods::getPrice))
map
map方法可以返回一個新的資料物件,組成一個新的Stream。
List<Goods> list = Arrays.asList(
new Goods("iphoneX",2999)
);
list.stream()
.map(goods -> goods.getGoodsName())
.forEach(System.out::println);複製程式碼
上面的示例演示的是從原有的商品物件中拿到商品名稱,然後組成一個新的List,其效果等同於
List<String> goodsNameList = new ArrayList<>(list.size());
for(Goods goods : list) {
goodsNameList.add(goods.getGoodsName());
}複製程式碼
map方法一般配合collect()方法一起使用
List<Goods> list = Arrays.asList(
new Goods("iphoneX",2999)
);
List<String> nameList = list.stream()
.map(goods -> goods.getGoodsName())
.collect(Collectors.toList());複製程式碼
collect(Collectors.toList())
的意思是將Stream中的元素轉換成List
flatMap
flatMap()方法是map()方法的扁平化處理,與map不同的是,flatMap把返回Stream物件操作交給開發者自己處理。看下面的例子:
Stream<String[]> stream = Stream.of("I am Java","hello world")
.map(s -> s.split(" "));複製程式碼
這個例子的本意是想要將每個字串進行拆分,把單詞單獨放入到Stream中,由於map返回的是一個字串陣列String[]
,因此得到的Stream物件的泛型引數就是Stream
,而不是Stream
。
解決辦法是使用flatMap:
Stream<String> stream2 = Stream.of("I am Java","hello world")
.flatMap(s -> Stream.of(s.split(" ")));
stream2.forEach(System.out::println);複製程式碼
列印:
I
am
Java
hello
world複製程式碼
綜合示例
下面來看一個綜合示例,演示的功能是:查詢商品名稱,價格大於3000,按價格降序
public class StreamTest3 {
@Data
@AllArgsConstructor
static class Goods {
private String goodsName;
private int price;
}
public static void main(String[] args) {
List<Goods> list = Arrays.asList(
new Goods("iphoneX",2999)
);
// 查詢商品名稱,價格大於3000,按價格降序
List<String> nameList = list.stream()
.filter(goods -> goods.getPrice() > 3000)
.sorted(Comparator.comparing(Goods::getPrice).reversed())
.map(Goods::getGoodsName)
.collect(Collectors.toList());
System.out.println(nameList);
}
}複製程式碼
列印:[mate30 pro,iphoneX]
程式碼對應的SQL為:
SELECT goods_name FROM goods WHERE price > 3000 ORDER BY price DESC複製程式碼
小結
本篇講解了如何建立Stream以及Stream一些常用方法的使用方式,我們將會在下一篇著重講解collect()
和reduce()
的用法。
定期分享技術乾貨,一起學習,一起進步!