【java代碼之美】---Java8 Stream
Stream
第一次看到Stream表達式就深深把我吸引,用它可以使你的代碼更加整潔而且對集合的操作效率也會大大提高,如果你還沒有用到java8的Stream特性,那就說明你確實out啦。
一、概述
1、什麽是Stream
Stream是一種可供流式操作的數據視圖有些類似數據庫中視圖的概念它不改變源數據集合如果對其進行改變的操作它會返回一個新的數據集合。
總的來講它有三大特性:在之後我們會對照著詳細說明
1、stream不存儲數據
2、stream不改變源數據
3、stream的延遲執行特性
2、Stream優點
-
代碼簡潔,函數式編程寫出的代碼簡潔且意圖明確,使用stream
-
多核友好,Java函數式編程使得編寫並行程序從未如此簡單,你需要的全部就是調用一下
parallel()
方法。
3、Stream API常用方法
Stream操作分類 | ||
中間操作(Intermediate operations) | 無狀態(Stateless) | unordered() filter() map() mapToInt() mapToLong() mapToDouble() flatMap() flatMapToInt() flatMapToLong() flatMapToDouble() peek() |
有狀態(Stateful) | distinct() sorted() sorted() limit() skip() | |
結束操作(Terminal operations) | 非短路操作 | forEach() forEachOrdered() toArray() reduce() collect() max() min() count() |
短路操作(short-circuiting) | anyMatch() allMatch() noneMatch() findFirst() findAny() |
Stream上的所有操作分為兩類:中間操作
中間操作又可以分為無狀態的和有狀態的:
無狀態中間操作是指元素的處理不受前面元素的影響,而有狀態的中間操作必須等到所有元素處理之後才知道最終結果,比如排序是有狀態操作,在讀取所有元素之前並不能確定排序結果;
結束操作又可以分為短路操作和非短路操作
短路操作是指不用處理全部元素就可以返回結果,比如找到第一個滿足條件的元素。之所以要進行如此精細的劃分,是因為底層對每一種情況的處理方式不同。
常用中間件
filter:過濾流,過濾流中的元素,返回一個符合條件的Stream
map:轉換流,將一種類型的流轉換為另外一種流。(mapToInt、mapToLong、mapToDouble 返回int、long、double基本類型對應的Stream)
flatMap:簡單的說,就是一個或多個流合並成一個新流。(flatMapToInt、flatMapToLong、flatMapToDouble 返回對應的IntStream、LongStream、DoubleStream流。)
distinct:返回去重的Stream。
sorted:返回一個排序的Stream。
peek:主要用來查看流中元素的數據狀態。
limit:返回前n個元素數據組成的Stream。屬於短路操作
skip:返回第n個元素後面數據組成的Stream。
結束操作
forEach: 循環操作Stream中數據。
toArray: 返回流中元素對應的數組對象。
reduce: 聚合操作,用來做統計。
collect: 聚合操作,封裝目標數據。
min、max、count: 聚合操作,最小值,最大值,總數量。
anyMatch: 短路操作,有一個符合條件返回true。
allMatch: 所有數據都符合條件返回true。
noneMatch: 所有數據都不符合條件返回true。
findFirst: 短路操作,獲取第一個元素。
findAny: 短路操作,獲取任一元素。
forEachOrdered: 暗元素順序執行循環操作。
二、各種案例說明
多舉點例子,以後忘記了還可以來找自己的博客,哈哈。
首先寫一個領域對象
public class Person { private Integer id; private String name; private String sex; private Integer age; //提供get,set,和滿參構造函數 }
1、map中間件相關例子
public class TestMap { public static void main(String[] args) { List<Person> persionList = new ArrayList<Person>(); persionList.add(new Person(1,"張三","男",8)); persionList.add(new Person(2,"小小","女",2)); persionList.add(new Person(3,"李四","男",25)); persionList.add(new Person(4,"王五","女",20)); persionList.add(new Person(5,"趙六","男",38)); persionList.add(new Person(6,"大大","男",65)); //1、只取出該集合中所有姓名組成一個新集合 List<String> nameList=persionList.stream().map(Person::getName).collect(Collectors.toList()); System.out.println(nameList.toString()); //2、只取出該集合中所有id組成一個新集合 List<Integer> idList=persionList.stream().mapToInt(Person::getId).boxed().collect(Collectors.toList()); System.out.println(idList.toString()); //3、list轉map,key值為id,value為Person對象 Map<Integer, Person> personmap = persionList.stream().collect(Collectors.toMap(Person::getId, person -> person)); System.out.println(personmap.toString()); //4、list轉map,key值為id,value為name Map<Integer, String> namemap = persionList.stream().collect(Collectors.toMap(Person::getId, Person::getName)); System.out.println(namemap.toString()); } }
運行結果:
是不是之前要好幾層的for循環解決的問題,通過Stream只要一行代碼就可以解決了。
這裏要註意,如果你list轉map的key如果不唯一,會報錯,所以如果你不確定你的key是否唯一,可以改成如下:
Map<Integer, String> map = persionList.stream().collect( Collectors.toMap(Person::getAge, Person::getName, (key1, key2) -> key1) );
2、filter相關例子
public class TestFilter { public static void main(String[] args) { List<Person> persionList = new ArrayList<Person>(); persionList.add(new Person(1, "張三", "男", 8)); persionList.add(new Person(2, "小小", "女", 2)); persionList.add(new Person(3, "李四", "男", 25)); persionList.add(new Person(4, "王五", "女", 8)); persionList.add(new Person(5, "趙六", "女", 25)); persionList.add(new Person(6, "大大", "男", 65)); //1、查找年齡大於20歲的人數 long age=persionList.stream().filter(p->p.getAge()>20).count(); System.out.println(age); //2、查找年齡大於20歲,性別為男的人數 List<Person> ageList=persionList.stream().filter(p->p.getAge()>20).filter(p->"男".equals(p.getSex())).collect(Collectors.toList()); System.out.println(ageList.size()); } /* *運行結果: * 3 * 2 */ }
3、sorted相關例子
對於數組舉例
public class TestSort { String[] arr1 = {"abc","a","bc","abcd"}; /** * 按照字符長度排序 */ @Test public void testSorted1_(){ Arrays.stream(arr1).sorted(Comparator.comparing(String::length)).forEach(System.out::println); //輸出:a、bc、abc、abcd } /** * 倒序 * reversed(),java8泛型推導的問題,所以如果comparing裏面是非方法引用的lambda表達式就沒辦法直接使用reversed() * Comparator.reverseOrder():也是用於翻轉順序,用於比較對象(Stream裏面的類型必須是可比較的) * Comparator. naturalOrder():返回一個自然排序比較器,用於比較對象(Stream裏面的類型必須是可比較的) */ @Test public void testSorted2_(){ Arrays.stream(arr1).sorted(Comparator.comparing(String::length).reversed()).forEach(System.out::println); //輸出:abcd、abc、bc、a Arrays.stream(arr1).sorted(Comparator.reverseOrder()).forEach(System.out::println); //輸出:bc、abcd、abc、a Arrays.stream(arr1).sorted(Comparator.naturalOrder()).forEach(System.out::println); //輸出:a、abc、abcd、bc } /** * 先按照首字母排序 * 之後按照String的長度排序 */ @Test public void testSorted3_(){ Arrays.stream(arr1).sorted(Comparator.comparing(this::com1).thenComparing(String::length)).forEach(System.out::println); }
//輸出:a、abc、abcd、bc public char com1(String x){ return x.charAt(0); } }
對於集合舉例
public class TestSort { public static void main(String[] args) { List<Person> persionList = new ArrayList<Person>(); persionList.add(new Person(1, "張三", "男", 8)); persionList.add(new Person(2, "小小", "女", 2)); persionList.add(new Person(3, "李四", "男", 25)); persionList.add(new Person(4, "王五", "女", 8)); persionList.add(new Person(5, "趙六", "女", 25)); persionList.add(new Person(6, "大大", "男", 65)); //1、找到年齡最小的歲數 Collections.sort(persionList, (x, y) -> x.getAge().compareTo(y.getAge())); Integer age = persionList.get(0).getAge(); System.out.println("年齡最小的有:" + age); //輸出:年齡最小的有:2 //2、找到年齡最小的姓名 String name = persionList.stream() .sorted(Comparator.comparingInt(x -> x.getAge())) .findFirst() .get().getName(); System.out.println("年齡最小的姓名:" + name); //輸出:年齡最小的姓名:小小 } }
其它的就不具體寫了。以後遇到特殊的再往裏面補充。
參考
1、跟上 Java 8 – Stream API 快速入門
2、java8之stream
3、[Java進階篇][函數式編程][Java 8+ Stream API]
4、深入理解Java Stream流水線
想太多,做太少,中間的落差就是煩惱。想沒有煩惱,要麽別想,要麽多做。中校【10】
【java代碼之美】---Java8 Stream