1. 程式人生 > 程式設計 >一起來學Java8(七)——Stream(上)

一起來學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()的用法。

定期分享技術乾貨,一起學習,一起進步!