Java8的新特性主要是Lambda表達式和流,當流和Lambda表達式結合起來一塊兒使用時,由於流申明式處理數據集合的特色,可讓代碼變得簡潔易讀
若是有一個需求,須要對數據庫查詢到的菜餚進行一個處理:java
public class Dish { private String name; private boolean vegetarian; private int calories; private Type type; // getter and setter }
private List<String> beforeJava7(List<Dish> dishList) { List<Dish> lowCaloricDishes = new ArrayList<>(); //1.篩選出卡路里小於400的菜餚 for (Dish dish : dishList) { if (dish.getCalories() < 400) { lowCaloricDishes.add(dish); } } //2.對篩選出的菜餚進行排序 Collections.sort(lowCaloricDishes, new Comparator<Dish>() { @Override public int compare(Dish o1, Dish o2) { return Integer.compare(o1.getCalories(), o2.getCalories()); } }); //3.獲取排序後菜餚的名字 List<String> lowCaloricDishesName = new ArrayList<>(); for (Dish d : lowCaloricDishes) { lowCaloricDishesName.add(d.getName()); } return lowCaloricDishesName; }
private List<String> afterJava8(List<Dish> dishList) { return dishList.stream() .filter(d -> d.getCalories() < 400) //篩選出卡路里小於400的菜餚 .sorted(comparing(Dish::getCalories)) //根據卡路里進行排序 .map(Dish::getName) //提取菜餚名稱 .collect(Collectors.toList()); //轉換爲List }
不拖泥帶水,一鼓作氣,原來須要寫24
代碼實現的功能如今只需5
行就能夠完成了
高高興興寫完需求這時候又有新需求了,新需求以下:數據庫
Map<Type, List<Dish>>
的結果這要是放在jdk8以前確定會頭皮發麻編程
private static Map<Type, List<Dish>> beforeJdk8(List<Dish> dishList) { Map<Type, List<Dish>> result = new HashMap<>(); for (Dish dish : dishList) { //不存在則初始化 if (result.get(dish.getType())==null) { List<Dish> dishes = new ArrayList<>(); dishes.add(dish); result.put(dish.getType(), dishes); } else { //存在則追加 result.get(dish.getType()).add(dish); } } return result; }
還好jdk8有Stream,不再用擔憂複雜集合處理需求數組
private static Map<Type, List<Dish>> afterJdk8(List<Dish> dishList) { return dishList.stream().collect(groupingBy(Dish::getType)); }
又是一行代碼解決了需求,忍不住大喊Stream API
牛批
看到流的強大功能了吧,接下來將詳細介紹流數據結構
流是從支持數據處理操做的源生成的元素序列,源能夠是數組、文件、集合、函數。流不是集合元素,它不是數據結構並不保存數據,它的主要目的在於計算dom
生成流的方式主要有五種ide
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> stream = integerList.stream();
經過集合的stream
方法生成流函數
int[] intArr = new int[]{1, 2, 3, 4, 5}; IntStream stream = Arrays.stream(intArr);
經過Arrays.stream
方法生成流,而且該方法生成的流是數值流【即IntStream
】而不是Stream<Integer>
。補充一點使用數值流能夠避免計算過程當中拆箱裝箱,提升性能。Stream API
提供了mapToInt
、mapToDouble
、mapToLong
三種方式將對象流【即Stream<T>
】轉換成對應的數值流,同時提供了boxed
方法將數值流轉換爲對象流性能
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
經過Stream
的of
方法生成流,經過Stream
的empty
方法能夠生成一個空流優化
Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())
經過Files.line
方法獲得一個流,而且獲得的每一個流是給定文件中的一行
提供了iterate
和generate
兩個靜態方法從函數中生成流
iterator
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
iterate
方法接受兩個參數,第一個爲初始化值,第二個爲進行的函數操做,由於iterator
生成的流爲無限流,經過limit
方法對流進行了截斷,只生成5個偶數
generator
Stream<Double> stream = Stream.generate(Math::random).limit(5);
generate
方法接受一個參數,方法參數類型爲Supplier<T>
,由它爲流提供值。generate
生成的流也是無限流,所以經過limit
對流進行了截斷
流的操做類型主要分爲兩種
filter
、map
等一個流有且只能有一個終端操做,當這個操做執行後,流就被關閉了,沒法再被操做,所以一個流只能被遍歷一次,若想在遍歷須要經過源數據在生成流。終端操做的執行,纔會真正開始流的遍歷。以下面即將介紹的count
、collect
等
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); System.out.println(integerList.stream().count());
經過使用count
方法計算出流中元素個數
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); integerList.stream().filter(i -> i > 3).forEach(System.out::println);
經過使用filter
方法進行條件篩選,filter
的方法參數爲一個條件
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); integerList.stream().distinct().forEach(System.out::println);
經過distinct
方法快速去除重複的元素
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); integerList.stream().limit(3).forEach(System.out::println);
經過limit
方法指定返回流的個數,limit
的參數值必須>=0
,不然將會拋出異常
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); integerList.stream().skip(2).forEach(System.out::println);
經過skip
方法跳過流中的元素,上述例子跳過前兩個元素,因此打印結果爲2,3,4,5
,skip
的參數值必須>=0
,不然將會拋出異常
所謂流映射就是將接受的元素映射成另一個元素
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action"); stringList.stream().map(String::length).forEach(System.out::println);
經過map
方法能夠完成映射,該例子完成中String -> Integer
的映射,以前上面的例子經過map
方法完成了Dish->String
的映射
將一個流中的每一個值都轉換爲另外一個流
List<String> stringList = Arrays.asList("Hello", "World"); List<Stream<String>> str = stringList.stream() .map(world -> world.split(" ")) .flatMap(Arrays::stream) .distinct() .collect(Collectors.toList());
map(world -> world.split(" "))
的返回值爲Stream<String[]>
,咱們想得到的結果爲Stream<String>
,能夠經過flatMap
方法完成Stream<String[]> ->Stream<String>
的轉換
提供了三種匹配方式
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().allMatch(i -> i > 3)) { System.out.println("值都大於3"); }
經過allMatch
方法實現
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().anyMatch(i -> i > 3)) { System.out.println("存在大於3的值"); }
等同於
for (Integer i : integerList) { if (i > 3) { System.out.println("存在大於3的值"); break; } }
存在大於3的值則打印,java8
中經過anyMatch
方法實現這個功能
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().noneMatch(i -> i > 3)) { System.out.println("值都小於3"); }
經過noneMatch
方法實現
提供了兩種查找方式
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); System.out.println(integerList.stream().filter(i -> i > 3).findFirst());
經過findFirst
方法查找到第一個大於三的元素並打印
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); System.out.println(integerList.stream().filter(i -> i > 3).findAny());
經過findAny
方法查找到其中一個大於三的元素並打印,由於內部進行優化的緣由,當找到第一個知足大於三的元素時就結束,該方法結果和findFirst
方法結果同樣。提供findAny
方法是爲了更好的利用並行流,findFirst
方法在並行上限制更多【本篇文章將不介紹並行流】
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> min = integerList.stream().min(Integer::compareTo); Optional<Integer> max = integerList.stream().max(Integer::compareTo);
min
獲取流中最小值,max
獲取流中最大值,方法參數爲Comparator<? super T> comparator
假設咱們對一個集合中的值進行求和
int sum = 0; for (int i : integerList) { sum += i; }
int sum = integerList.stream().reduce(0, (a, b) -> (a + b));
一行就能夠完成,還可使用方法引用簡寫成:
int sum = integerList.stream().reduce(0, Integer::sum);
reduce
接受兩個參數,一個初始值這裏是0
,一個BinaryOperator<T> accumulator
來將兩個元素結合起來產生一個新值
另外reduce
方法還有一個沒有初始化值的重載方法,經過reduce
咱們還能夠進行求最大值和最小值
Optional<Integer> min = integerList.stream().reduce(Integer::min); Optional<Integer> max = integerList.stream().reduce(Integer::max);
Map<Type, List<Dish>> result = dishList.stream().collect(groupingBy(Dish::getType));
在collect
方法中傳入groupingBy
進行分組,其中groupingBy
的方法參數爲分類函數。還能夠經過嵌套使用groupingBy
進行多級分類
Map<Type, List<Dish>> result = menu.stream().collect(groupingBy(Dish::getType, groupingBy(dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET; else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; else return CaloricLevel.FAT; })));
分區是特殊的分組,它分類依據是true和false,因此返回的結果最多能夠分爲兩組
Map<Boolean, List<Dish>> result = menu.stream().collect(partitioningBy(Dish :: isVegetarian))
等同於
Map<Boolean, List<Dish>> result = menu.stream().collect(groupingBy(Dish :: isVegetarian))
這個例子可能並不能看出分區和分類的區別,甚至以爲分區根本沒有必要,換個明顯一點的例子:
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Map<Boolean, List<Integer>> result = integerList.stream().collect(partitioningBy(i -> i < 3));
返回值的鍵仍然是布爾類型,可是它的分類是根據範圍進行分類的,分區比較適合處理根據範圍進行分類
經過使用Stream API
能夠簡化代碼,同時提升了代碼可讀性,趕忙在項目裏用起來。講道理在沒學Stream API
以前,誰要是給我在應用裏寫不少Lambda
,Stream API
,飛起就想給他一腳。我想,我如今可能愛上他了【嘻嘻】。同時使用的時候注意不要將聲明式和命令式編程混合使用,前幾天刷segment
刷到一條:imango
老哥說的很對,別用聲明式編程的語法幹命令式編程的勾