在實際開發中常常須要獲取各類各樣不一樣格式的數據,由於數據庫的表結構是一開始就設計好的因此不少時候咱們不得不先從數據庫裏或其餘地方得到數據後再根據需求去一層一層的篩選數據,在Java 8以前的作法不外乎就是各類List、Set一塊兒上,各類循環判斷。若是隻是簡單的需求還好說,循環個一兩次再判斷一下就能夠解決,可是需求複雜的話就會寫出很複雜的代碼出來,時間久了後不只本身看不懂,並且由於代碼太複雜測試的時候不敢保證覆蓋到全部的地方,萬一代碼到了線上出問題,不只很差改還可能影響到之前的功能,而在Java 8中就提供了新的特性stream,專門用於對集合中的數據進行篩選、分類等操做,下面咱們就會介紹stream的使用。java
咱們來看下面這段代碼,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());
講到這裏你們可能會產生疑惑,流究竟是什麼,爲何它能夠進行這樣的操做。簡單來講流其實就是 「從支持數據處理操做的源生成的元素序列」 ,這句話究竟是什麼意思呢?下面咱們來看看流的各類特性就明白了。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 );
流和集合不同,集合能夠想遍歷多少次就遍歷多少次,可是流只能遍歷一次,若是你作了下面這樣的操做,那代碼會拋出一個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);
仍是繼續看這段代碼,其中filter
、map
和limit
操做被稱爲 中間操做 , 中間操做 會返回一個新的流,而collect
則被稱爲 終端操做 ,只有終端操做纔會讓整個流執行並關閉,也就是說只有當collect
被執行時filter
、map
和limit
纔會被執行,上面講到流只能遍歷一次就是由於forEach
就是一個終端操做,執行完後就關閉流了。code
List<String> list = menu.stream() //篩選出卡路里高於300的元素 .filter(d -> d.getCalories() > 300) //獲取名字組成的流 .map(Dish::getName) //只取前3個 .limit(3) //返回List格式 .collect(toList());
以上就是stream的基本使用方法了,固然它的功能還遠遠不止這些,後面咱們還會講到篩選、分組、查找等更強大的操做。對象