Stream相關API學習使用記錄--(一)
一、前言
在以前的工作中,業務中很多時候是需要迴圈來獲取某一個列表中的一些資料,比如獲取某一個值做業務邏輯的判斷,或者獲取其中一部分的資料,如使用狀態的標誌位等等,這個時候可能很多時候都是使用簡單的for迴圈、Foreach迴圈或者迭代器等方式,很多時候為了處理業務都是需要迴圈巢狀迴圈的。但是在Java8中為集合的相關處理提供了一個更強大的工具,那麼就是Stream,在瀏覽程式碼的時候看見了其他人的書寫,感覺很驚奇(此時自己也很無語,Java8出來這麼久了,自己好像好沒有意識到它到底添加了那些新的內容,有什麼新的特性,簡化了那些操作等等),所以自己就對這個東西充滿了好奇。
二、 Stream的基礎簡介
2.1、stream是什麼
它是Java8 API新增的一個新的抽象流,它把要處理的元素看做一種流,流在管道中傳輸,並且可以在管道的節點上進行處理,例如排序,篩選和聚合等操作,可以讓人以一種宣告的方式來處理資料。
元素流在管道中經過中間操作(intermediate operation)處理,最後由最終操作(terminal operation)得到前面的處理結果。
+------------------------+ +---------+ +-----------+ +--------+ +-------+
| stream of elements +--------> |filter +-----> |sorted +-----> |map +----> |collect|
+-----------------------+ +---------+ +-----------+ +--------+ +-------+
2.2、特性
stream是一個來自資料來源的元素佇列並支援聚合操作:
(1)元素是特定的元素,形成一個佇列,但是stream不會儲存元素,而是按需計算
(2)資料來源可以是集合、陣列、I/Ochannel等
(3)集合操作類似鏈式的SQL,如filter、map、reduce、find、match、sortde等
2.3、流生成方式
2.3.1、序列流
使用stream方式,為集合建立序列流
2.3.2、並行流
使用parallelStream 為集合建立並行流
三、管道節點
3.1、ForEach
Stream提供了新的方法來迭代流中的每一個數據
public classStreamDemoTest { private final static Integer MAX_CIRCLE = 100; public static void main(String[] args) { List<Integer> strList = new ArrayList<>(); Map<Integer, String> integerMap = new HashMap<>(); for (int i = 0; i < MAX_CIRCLE; i++) { strList.add(i); integerMap.put(i, i+"e"); } strList.stream().forEach(s -> System.out.println(s)); } }
3.2、map
map方法用於對映每個元素到對應的結果
public class StreamDemoTest { private final static Integer MAX_CIRCLE = 100; public static void main(String[] args) { List<Integer> strList = new ArrayList<>(); Map<Integer, String> integerMap = new HashMap<>(); for (int i = 0; i < MAX_CIRCLE; i++) { strList.add(i); integerMap.put(i, i+"e"); } //strList.stream().forEach(s -> System.out.println(s)); List<Integer> integerList = strList.stream().map(i -> i * i).collect(Collectors.toList()); integerList.stream().forEach(s -> System.out.println(s)); } }
3.3、filter
filter用於通過設定的條件過濾出元素
List<String> stringList = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 獲取空字串的數量 long count = stringList.stream().filter(string ->string!="abc").count(); //獲取不是abc和空的字串 List<String> list = stringList.stream().filter(str -> str != "abc"&&str!="").collect(Collectors.toList());
3.4、limit
用於獲取指定數量的流
List<String> stringList = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 獲取空字串的數量 long count = stringList.stream().filter(string ->string!="abc").count(); //限定獲取過濾條件後的數量 List<String> list = stringList.stream().filter(str -> str != "abc"&&str!="").limit(3).collect(Collectors.toList()); list.stream().forEach(s-> System.out.println(s));
limit的位置可以在filter前面或者後面,只是表示的含義不一樣而已。
3.4、sorted
sorted用於對流進行排序
public static void main(String[] args) { List<String> strList = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); //自然排序 System.out.println("自然排序"); strList.stream().sorted().forEach(s -> System.out.println(s)); System.out.println("倒序排序"); strList.stream().sorted(Comparator.reverseOrder()).forEach(s -> System.out.println(s)); }
輸出結果已經按照要求進行了排序,預設的是升序排列了,如果想簡單的倒序排序,可以使用 Stream<T> sorted(Comparator<? super T> comparator)方法,裡面採用Comparator.reverseOrder()進行倒序排序。
如果是自定義的其他的型別,怎麼進行排序那?
下面是自定義的一個使用者資訊的簡單實體
class User { /** * 姓名 **/ private String userName; /** * 英文名稱 **/ private String englishName; /** * 年齡 **/ private int age; /** * 住址 **/ private String address; /** * 電話 **/ private String tel; public User(String userName, String englishName, int age) { this.userName = userName; this.englishName = englishName; this.age = age; } public User(String userName, String englishName, int age, String address, String tel) { this.userName = userName; this.englishName = englishName; this.age = age; this.address = address; this.tel = tel; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getEnglishName() { return englishName; } public void setEnglishName(String englishName) { this.englishName = englishName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getTel() { return tel; } public void setTel(String tel) { this.tel = tel; } @Override public String toString() { return "User{" + "userName='" + userName + '\'' + ", englishName='" + englishName + '\'' + ", age=" + age + ", address='" + address + '\'' + ", tel='" + tel + '\'' + '}'; } }
demo示例
1 public static void main(String[] args) { 2 /* List<String> strList = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); 3 //自然排序 4 System.out.println("自然排序"); 5 strList.stream().sorted().forEach(s -> System.out.println(s)); 6 7 System.out.println("倒序排序"); 8 strList.stream().sorted(Comparator.reverseOrder()).forEach(s -> System.out.println(s));*/ 9 10 List<User> userList = new ArrayList<>(); 11 User user = new User("Angle", "B", 10); 12 userList.add(user); 13 user = new User("Heni", "G", 39); 14 userList.add(user); 15 user = new User("Eson", "F", 6); 16 userList.add(user); 17 user = new User("Cily", "D", 2); 18 userList.add(user); 19 user = new User("Cily", "G", 6); 20 userList.add(user); 21 System.out.println("正常輸出"); 22 userList.stream().forEach(s-> System.out.println(s.toString())); 23 24 System.out.println("按照名稱預設升序排列"); 25 userList.stream().sorted(Comparator.comparing(User::getUserName)).forEach(s -> System.out.println(s)); 26 27 System.out.println("按照名稱進行倒序排列"); 28 userList.stream().sorted(Comparator.comparing(User::getUserName).reversed()).forEach(s -> System.out.println(s)); 29 30 System.out.println("多元素排列"); 31 userList.stream().sorted(Comparator.comparing(User::getUserName).thenComparing(User::getAge).reversed()).forEach(s -> System.out.println(s)); 32 }
輸出結果:
正常輸出 User{userName='Angle', englishName='B', age=10, address='null', tel='null'} User{userName='Heni', englishName='G', age=39, address='null', tel='null'} User{userName='Eson', englishName='F', age=6, address='null', tel='null'} User{userName='Cily', englishName='D', age=2, address='null', tel='null'} User{userName='Cily', englishName='G', age=6, address='null', tel='null'} 按照名稱預設升序排列 User{userName='Angle', englishName='B', age=10, address='null', tel='null'} User{userName='Cily', englishName='D', age=2, address='null', tel='null'} User{userName='Cily', englishName='G', age=6, address='null', tel='null'} User{userName='Eson', englishName='F', age=6, address='null', tel='null'} User{userName='Heni', englishName='G', age=39, address='null', tel='null'} 按照名稱進行倒序排列 User{userName='Heni', englishName='G', age=39, address='null', tel='null'} User{userName='Eson', englishName='F', age=6, address='null', tel='null'} User{userName='Cily', englishName='D', age=2, address='null', tel='null'} User{userName='Cily', englishName='G', age=6, address='null', tel='null'} User{userName='Angle', englishName='B', age=10, address='null', tel='null'} 多元素排列 User{userName='Heni', englishName='G', age=39, address='null', tel='null'} User{userName='Eson', englishName='F', age=6, address='null', tel='null'} User{userName='Cily', englishName='G', age=6, address='null', tel='null'} User{userName='Cily', englishName='D', age=2, address='null', tel='null'} User{userName='Angle', englishName='B', age=10, address='null', tel='null'}
可以看出來,整個排序是相當的簡單了,不需要自己的類繼承comparable,也不需要自己擴充套件排序comparator了。
對於map型別格式的資料。可以對鍵排序或者值排序
1 public static void main(String[] args) { 2 Map<String, String> map = new LinkedHashMap<String, String>(); 3 map.put("12", "ddd"); 4 map.put("23", "eee"); 5 map.put("34", "ddw"); 6 map.put("21", "ecc"); 7 8 System.out.println("根據值排序"); 9 map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getValue)).forEach(e -> { 10 String key = e.getKey(); 11 String value = e.getValue(); 12 System.out.println("鍵值"+key+"值"+value); 13 }); 14 System.out.println("根據鍵排序"); 15 map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach(e -> { 16 //System.out.println(e.toString()); 17 String key = e.getKey(); 18 String value = e.getValue(); 19 System.out.println("鍵值"+key+"值"+value); 20 }); 21 }
輸出的結果:
根據值排序
鍵值12值ddd
鍵值34值ddw
鍵值21值ecc
鍵值23值eee
根據鍵排序
鍵值12值ddd
鍵值21值ecc
鍵值23值eee
鍵值34值ddw
綜合看下來,使用對應的Stream比使用平常簡單的迴圈而言程式碼量少了很多。