一起來學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<T, R> Stream 返回新的數據
flatMap Function<T, R> Stream<T, 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<T, A, R> 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,2,3,3,4)
	.distinct()
	.forEach(System.out::println)

打印

1
2
3
4

sorted

使用sorted方法可以對元素進行排序操作

Stream.of(6,1,7,2,8,5)
	.sorted()
	.forEach(System.out::println);

打印

1
2
5
6
7
8

sorted()默認是從小到大排列,如果要從大到小降序,可以使用.sorted(Comparator.reverseOrder())

Stream.of(6,1,7,2,8,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", 4000)
		, new Goods("mate30 pro", 5999)
		, new Goods("redmek20", 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", 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中的元素轉換成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<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

綜合示例

下面來看一個綜合示例,演示的功能是:查詢商品名稱,價格大於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", 4000)
                , new Goods("mate30 pro", 5999)
                , new Goods("redmek20", 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()的用法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章