一起來學(xué)Java8(七)——Stream(上)

從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):猿敲月下碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末早像,一起剝皮案震驚了整個(gè)濱河市僻肖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卢鹦,老刑警劉巖臀脏,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異冀自,居然都是意外死亡揉稚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門熬粗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搀玖,“玉大人,你說我怎么就攤上這事驻呐」嘧纾” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵含末,是天一觀的道長猜拾。 經(jīng)常有香客問我,道長佣盒,這世上最難降的妖魔是什么关带? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮沼撕,結(jié)果婚禮上宋雏,老公的妹妹穿的比我還像新娘。我一直安慰自己务豺,他們只是感情好磨总,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著笼沥,像睡著了一般蚪燕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奔浅,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天馆纳,我揣著相機(jī)與錄音,去河邊找鬼汹桦。 笑死鲁驶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的舞骆。 我是一名探鬼主播钥弯,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼径荔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脆霎?” 一聲冷哼從身側(cè)響起总处,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎睛蛛,沒想到半個(gè)月后鹦马,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忆肾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年荸频,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片难菌。...
    茶點(diǎn)故事閱讀 38,643評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蔑滓,靈堂內(nèi)的尸體忽然破棺而出郊酒,到底是詐尸還是另有隱情,我是刑警寧澤键袱,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布燎窘,位于F島的核電站,受9級特大地震影響蹄咖,放射性物質(zhì)發(fā)生泄漏褐健。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一澜汤、第九天 我趴在偏房一處隱蔽的房頂上張望蚜迅。 院中可真熱鬧,春花似錦俊抵、人聲如沸谁不。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刹帕。三九已至,卻和暖如春谎替,著一層夾襖步出監(jiān)牢的瞬間偷溺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工钱贯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挫掏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓秩命,卻偏偏與公主長得像砍濒,于是被迫代替她去往敵國和親淋肾。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容