從Java8開始训挡,新增了一個(gè)java.util.stream
包规辱,這個(gè)包下的類和接口用來處理集合中的元素赊舶,在這個(gè)包下面有一個(gè)Stream接口派任,我們主要使用這個(gè)接口來對集合進(jìn)行操作摸恍。
創(chuàng)建Stream
首先來看下創(chuàng)建Stream有哪幾種方式悉罕。
使用Stream自帶的靜態(tài)方法生成Stream對象赤屋,常見的靜態(tài)方法有以下幾個(gè):
- Stream.of(T)
- Stream.of(T... values)
- Stream.generate(Supplier)
- Stream.iterate(T, UnaryOperator)
- Stream.empty()
現(xiàn)在來看下每個(gè)靜態(tài)方法的作用
Stream.of(T) & Stream.of(T... values)
Stream.of是由兩個(gè)重載方法組成,一個(gè)傳入單值壁袄,一個(gè)傳入數(shù)組
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可以用來生成具有多個(gè)元素的Stream类早,如果不加控制會(huì)一直生成下去,一般配合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方法第一參數(shù)設(shè)定一個(gè)初始值嗜逻,第二個(gè)參數(shù)表示基于這個(gè)初始值涩僻,每次循環(huán)后返回一個(gè)新的值替換這個(gè)初始值。limit(5)表示循環(huán)5次結(jié)束栈顷,最后Stream中包含了5個(gè)元素逆日。
再來看下Stream.generate
Stream.generate方法只有一個(gè)Supplier參數(shù),意思是每次循環(huán)執(zhí)行Supplier接口方法返回一個(gè)新的值萄凤,放入到Stream中室抽,由于Supplier是一個(gè)函數(shù)式接口,因此可以直接寫成Lambda表達(dá)式
AtomicInteger i = new AtomicInteger();
Stream.generate(()-> {
return i.getAndIncrement();
})
.limit(5)
.forEach(System.out::println);
上面的代碼同樣打印0~4靡努。
除了Stream靜態(tài)方法之外狠半,還可以使用Collection接口中的stream()方法來生成Stream對象。
Collection<String> list = Arrays.asList("hello", "world");
Stream streamList = list.stream();
同理颤难,只要是Collection接口的子類或?qū)崿F(xiàn)類都可以使用stream()方法神年。
操作Stream
Stream中的方法有很多,大致歸納如下表格所示:
方法 | 方法參數(shù) | 返回類型 | 描述 |
---|---|---|---|
filer | Predicate<T> | Stream<T> | 過濾數(shù)據(jù) |
distinct | 無 | Stream<T> | 去重 |
map | Function<T, R> | Stream<R> | 返回新的數(shù)據(jù) |
flatMap | Function<T, R> | Stream<T, Stream<R>> | 返回新的數(shù)據(jù)行嗤,并做扁平化處理 |
sort | Comparator<T> | Stream<T> | 對數(shù)據(jù)進(jìn)行排序操作 |
limit | long | Stream<T> | 截取前幾條數(shù)據(jù) |
skip | long | Stream<T> | 跳過幾條數(shù)據(jù) |
anyMatch | Predicate<T> | boolean | 匹配任意一條數(shù)據(jù)已日,如果匹配到返回true |
noneMatch | Predicate<T> | boolean | 如果沒有匹配到數(shù)據(jù),返回true |
allMatch | Predicate<T> | boolean | 如果所有數(shù)據(jù)全部匹配到栅屏,返回true |
findAny | 無 | Optional<T> | 返回任意一條數(shù)據(jù) |
findFirst | 無 | Optional<T> | 返回第一條數(shù)據(jù) |
count | 無 | long | 返回元素個(gè)數(shù) |
forEach | Consumer<T> | void | 遍歷元素飘千,執(zhí)行Consumer |
collect | Collector<T, A, R> | R | 元素收集 |
reduce | BinaryOperator<T> | Optional<T> | 數(shù)據(jù)匯總 |
從方法的返回結(jié)果可以看出,這些方法可以分為兩大類栈雳,一類是返回Stream對象护奈,可以繼續(xù)對Stream操作,這類方法也被稱之為中間操作(Intermediate operations)
哥纫,另一類是返回非Stream霉旗,結(jié)束操作,這類方法也被稱之為中端操作(Terminal operations)
蛀骇,這兩類方法往往一起配合操作厌秒。
下面我們挑選其中的幾個(gè)方法來演示它們的作用。
filter
filter方法用來篩選出我們想要的數(shù)據(jù)擅憔,方法參數(shù)是一個(gè)Predicate接口鸵闪,因?yàn)镻redicate是一個(gè)函數(shù)式接口,我們可以使用Lambda表達(dá)式來寫暑诸。
Integer[] arr = { 1, 2, 3, 4, 5 };
long count = Stream.of(arr)
.filter(i -> i % 2 == 0)
.count();
System.out.println("偶數(shù)數(shù)量:" + count);
在這個(gè)例子中蚌讼,我們篩選出了偶數(shù)數(shù)字辟灰,并且統(tǒng)計(jì)出偶數(shù)的數(shù)量。如果要打印每個(gè)偶數(shù)篡石,可以使用forEach方法
Stream.of(arr)
.filter(i -> i % 2 == 0)
.forEach(System.out::println);
打由【亍:
2
4
如果要查找任意一個(gè)元素,可以使用findAny
int num = Stream.of(arr)
.filter(i -> i % 2 == 0)
.findAny()
.orElse(0);
System.out.println("findAny:" + num);
注意夏志,findAny()返回的是一個(gè)Optional對象,因?yàn)橛锌赡軟]有找到數(shù)據(jù)苛让,因此需要開發(fā)者自己處理沒有找到數(shù)據(jù)的情況沟蔑。同理findFirst
也是返回一個(gè)Optional對象。
distinct
distinct方法會(huì)對元素進(jìn)行去重操作狱杰,類似于SQL中的SELECT distinct xx
Stream.of(1,1,2,3,3,4)
.distinct()
.forEach(System.out::println)
打印
1
2
3
4
sorted
使用sorted方法可以對元素進(jìn)行排序操作
Stream.of(6,1,7,2,8,5)
.sorted()
.forEach(System.out::println);
打印
1
2
5
6
7
8
sorted()默認(rèn)是從小到大排列瘦材,如果要從大到小降序,可以使用.sorted(Comparator.reverseOrder())
Stream.of(6,1,7,2,8,5)
.sorted(Comparator.reverseOrder())
.forEach(System.out::println);
可以看到仿畸,sorted方法允許傳入一個(gè)比較器Comparator
讓開發(fā)者自己實(shí)現(xiàn)排序邏輯食棕。下面是一個(gè)自定義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);
這個(gè)列子演示了按商品價(jià)格從低到高排序。此處的sorted部分可以簡化為:.sorted(Comparator.comparing(Goods::getPrice))
map
map方法可以返回一個(gè)新的數(shù)據(jù)對象错沽,組成一個(gè)新的Stream簿晓。
List<Goods> list = Arrays.asList(
new Goods("iphoneX", 4000)
, new Goods("mate30 pro", 5999)
, new Goods("redmek20", 2999)
);
list.stream()
.map(goods -> goods.getGoodsName())
.forEach(System.out::println);
上面的示例演示的是從原有的商品對象中拿到商品名稱,然后組成一個(gè)新的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", 4000)
, new Goods("mate30 pro", 5999)
, new Goods("redmek20", 2999)
);
List<String> nameList = list.stream()
.map(goods -> goods.getGoodsName())
.collect(Collectors.toList());
collect(Collectors.toList())
的意思是將Stream中的元素轉(zhuǎn)換成List
flatMap
flatMap()方法是map()方法的扁平化處理憔儿,與map不同的是,flatMap把返回Stream對象操作交給開發(fā)者自己處理放可≮司剩看下面的例子:
Stream<String[]> stream = Stream.of("I am Java", "hello world")
.map(s -> s.split(" "));
這個(gè)例子的本意是想要將每個(gè)字符串進(jìn)行拆分,把單詞單獨(dú)放入到Stream中耀里,由于map返回的是一個(gè)字符串?dāng)?shù)組String[]
蜈缤,因此得到的Stream對象的泛型參數(shù)就是Stream<String[]>
,而不是Stream<String>
冯挎。
解決辦法是使用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
綜合示例
下面來看一個(gè)綜合示例,演示的功能是:查詢商品名稱房官,價(jià)格大于3000叠艳,按價(jià)格降序
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", 4000)
, new Goods("mate30 pro", 5999)
, new Goods("redmek20", 2999)
);
// 查詢商品名稱,價(jià)格大于3000易阳,按價(jià)格降序
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]
代碼對應(yīng)的SQL為:
SELECT goods_name FROM goods WHERE price > 3000 ORDER BY price DESC
小結(jié)
本篇講解了如何創(chuàng)建Stream以及Stream一些常用方法的使用方式,我們將會(huì)在下一篇著重講解collect()
和reduce()
的用法潦俺。
定期分享技術(shù)干貨拒课,一起學(xué)習(xí)徐勃,一起進(jìn)步!微信公眾號(hào):猿敲月下碼