Java 8之stream介紹和使用

前言:

在實際開發中常常須要獲取各類各樣不一樣格式的數據,由於數據庫的表結構是一開始就設計好的因此不少時候咱們不得不先從數據庫裏或其餘地方得到數據後再根據需求去一層一層的篩選數據,在Java 8以前的作法不外乎就是各類List、Set一塊兒上,各類循環判斷。若是隻是簡單的需求還好說,循環個一兩次再判斷一下就能夠解決,可是需求複雜的話就會寫出很複雜的代碼出來,時間久了後不只本身看不懂,並且由於代碼太複雜測試的時候不敢保證覆蓋到全部的地方,萬一代碼到了線上出問題,不只很差改還可能影響到之前的功能,而在Java 8中就提供了新的特性stream,專門用於對集合中的數據進行篩選、分類等操做,下面咱們就會介紹stream的使用。java


stream使用:

咱們來看下面這段代碼,Dish裏面有一個屬性calories表明菜品的卡路里值,如今的需求是按卡路里對菜品進行排序再返回菜名,而且要求卡路里的值大於400。咱們能夠看到下面的操做很是繁瑣,先篩選出卡路里大於400的放入集合中,而後對這個集合進行排序,最後循環這個集合把名字放入一個新的集合,這是一個很常見的代碼,在Java 8以前都是這麼寫的,可是這還只是對一個屬性的篩選、排序就這麼繁瑣了,若是是多個屬性會更加麻煩,下面咱們就看看用stream是怎麼實現的。數據庫

//將卡路里低於400的對象都放到集合中
List<Dish> lowCaloricDishes = new ArrayList<>();
    for(Dish d: menu){
        if(d.getCalories() < 400){
            lowCaloricDishes.add(d);
        }
    }

//把這個集合排序
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
    public int compare(Dish d1, Dish d2){
        return Integer.compare(d1.getCalories(), d2.getCalories());
    }
});

//再新建一個String類型的集合來存放菜名
List<String> lowCaloricDishesName = new ArrayList<>();
for(Dish d: lowCaloricDishes){
    lowCaloricDishesName.add(d.getName());
}

這是Java 8中stream的用法,咱們會發現這段代碼寫起來很是舒服,首先調用stream()方法獲取了集合menu的流,而後調用了filter方法來篩選出卡路里超過400的元素,接着調用了sorted方法對篩選出來的元素進行排序,再調用map方法把篩選出來的元素裏面的name屬性抽出來做爲一個新的流,最後一步則是調用collect方法把存放name的流轉爲List格式返回,獲得的結果和上面如出一轍,可是整個步驟一目瞭然,先作什麼後作什麼,很是清晰,這就是stream的用法。測試

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;

List<Dish> menu = ...

List<String> lowCaloricDishesName = menu.stream()
    //篩選出卡路里大於400的
    .filter(d -> d.getCalories() < 400)
    //按卡路里值排序
    .sorted(comparing(Dish::getCalories))
    //抽取名字屬性建立一個新的流
    .map(Dish::getName)
    //這個流按List類型返回
    .collect(toList());

stream的定義:

講到這裏你們可能會產生疑惑,流究竟是什麼,爲何它能夠進行這樣的操做。簡單來講流其實就是 「從支持數據處理操做的源生成的元素序列」 ,這句話究竟是什麼意思呢?下面咱們來看看流的各類特性就明白了。spa

  • 元素序列,能夠經過流訪問到特定類型的一組有序的值,就像咱們經過集合訪問這些數據同樣,只不過集合是爲了存儲數據,而流則注重與對數據的計算和處理。
  • 源,流能夠會從一個數據源那裏獲取數據並生成流,例如上面講的從集合中獲取,生成的流裏面的元素和數據源裏的一致。
  • 數據處理操做,就像第一條裏說的,流是注重與對數據的計算和處理,因此它有不少不一樣的方法能夠對數據進行操做,例如篩選、分類、排序等等。
  • 流水線,顧名思義,流的不少操做都會返回一個新的流,因此咱們上面可使用連綴的方式調用那些方法,其實每一個操做都是基於它前面的那個操做返回的流進行的。

咱們來看下面的這段代碼和它的流程圖。它一開始從menu這個集合中獲取到了流,流裏面的數據和集合是相同的,而後調用filter方法以後篩選出符合條件的元素組成一個新的流傳遞給了map方法,而後map方法又從這個流裏面把元素的名字取出來組成一個新流傳遞給limit方法,limit方法則只取了前3個組成一個流傳遞給collect方法而後返回一個List,這就是流的工做原理設計

List<String> list = menu.stream()
    //篩選出卡路里高於300的元素
    .filter(d -> d.getCalories() > 300)
    //獲取名字組成的流
    .map(Dish::getName)
    //只取前3個
    .limit(3)
    //返回List格式
    .collect(toList());
System.out.println(list );

clipboard.png


stream只能遍歷一次:

流和集合不同,集合能夠想遍歷多少次就遍歷多少次,可是流只能遍歷一次,若是你作了下面這樣的操做,那代碼會拋出一個java.lang.IllegalStateException異常,流已被操做或關閉。3d

List<String> title = Arrays.asList("Java8", "In", "Action");
Stream<String> s = title.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);

stream的兩種操做:

仍是繼續看這段代碼,其中filtermaplimit操做被稱爲 中間操做中間操做 會返回一個新的流,而collect則被稱爲 終端操做 ,只有終端操做纔會讓整個流執行並關閉,也就是說只有當collect被執行時filtermaplimit纔會被執行,上面講到流只能遍歷一次就是由於forEach就是一個終端操做,執行完後就關閉流了。code

List<String> list = menu.stream()
    //篩選出卡路里高於300的元素
    .filter(d -> d.getCalories() > 300)
    //獲取名字組成的流
    .map(Dish::getName)
    //只取前3個
    .limit(3)
    //返回List格式
    .collect(toList());

以上就是stream的基本使用方法了,固然它的功能還遠遠不止這些,後面咱們還會講到篩選、分組、查找等更強大的操做。對象

相關文章
相關標籤/搜索