了解Stream
? Java8中有兩個最為重要的改變口猜,一個是Lambda表達式,另一個就是Stream API,針對常見的集合數(shù)據(jù)處理填硕,Stream API 提供了一種高效且易于使用的數(shù)據(jù)處理方式。
什么是Stream
基本概念
? 流(Stream)用于操作數(shù)據(jù)源所生成的元素序列。Java 8給Collection接口增加了兩個默認方法确垫,它們可以返回一個Stream
default Stream<E> stream() {
? return StreamSupport.stream(spliterator(), false);
}//stream()返回的是一個順序流default Stream<E> parallelStream() {
? return StreamSupport.stream(spliterator(), true);
}//parallelStream()返回的是一個并發(fā)流
- Stream 自己不會存儲元素。
- Stream 不會改變源對象帽芽。相反删掀,他們會返回一個持有結果的新Stream。
- Stream 操作是延遲執(zhí)行的导街。這意味著他們會等到需要結果的時候才執(zhí)行披泪。
基本示例
首先這里有一個Employee類
public class Employee {
private int id;
private String name;
private int age;
private double salary;
/*省略getter setter Constructor*/
}
//Employee列表
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66),
new Employee(101, "張三", 18, 9999.99),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "趙六", 20, 7777.77),
new Employee(104, "趙六", 19, 7777.77),
new Employee(104, "趙四", 40, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
返回薪資大于5000的員工列表,java8以前是這樣做的
List<Employee> newEmps = new ArrayList<>();
for(Employee emp : emps){
if(emp.salary > 5000.00){
newEmps.add(emp);
}
}
使用Stream API ,代碼可以這樣
List<Employee> newEmps = emps.stream()
.filter(s -> s.getSalary() > 5000.00)
.collect(Collectors.toList());
先通過stream()得到一個Stream對象,然后調用Stream上的方法搬瑰,filter()過濾得到薪資大于5000的,它的返回值依然是一個Stream,然后通過調用collect()方法并傳遞一個Collectors.toList()將結果集存放到一個List中.
使用Stream API處理集合類代碼更加簡潔易讀.
下面介紹一下Stream中的兩種操作
Stream的中間操作和終止操作
中間操作:
? 多個中間操作可以連接起來形成一個流水線款票,除非流水線上觸發(fā)終止操作,否則中間操作不會執(zhí)行任何的處理跌捆!而在終止操作時一次性全部處理徽职,稱為“惰性求值”
方法 | 描述 |
---|---|
filter(Predicate p) | 接收 Lambda , 從流中排除某些元素佩厚。 |
distinct() | 篩選姆钉,通過流所生成元素的 hashCode() 和 equals() 去 |
limit(long maxSize) | 截斷流,使其元素不超過給定數(shù)量抄瓦。 |
map(Function f) | 接收一個函數(shù)作為參數(shù)潮瓶,該函數(shù)會被應用到每個元素上,并將其映射成一個新的元素钙姊。 |
flatMap(Function f) | 接收一個函數(shù)作為參數(shù)毯辅,將流中的每個值都換成另一個流,然后把所有流連接成一個流 |
sorted(Comparator comp) | 產(chǎn)生一個新流煞额,其中按比較器順序排序 |
sorted() | 產(chǎn)生一個新流思恐,其中按自然順序排序 |
終止操作:
? 終端操作會從流的流水線生成結果。其結果可以是任何不是流的值膊毁,例如:List胀莹、Integer,甚至是void 婚温。
方法 | 描述 |
---|---|
forEach(Consumer c) | 內(nèi)部迭代 |
collect(Collector c) | 將流轉換為其他形式描焰。接收一個 Collector接口的實現(xiàn),用于給Stream中元素做匯總的方法 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
count() | 返回流中元素總數(shù) |
收集 : collect(Collector c)方法需要一個Collector 作為參數(shù),Collector 接口中方法的實現(xiàn)決定了如何對流執(zhí)行收集操作(如收集到 List栅螟、Set荆秦、Map)篱竭。Java8中提供了一個Collectors工具類, 工具中提供了很多靜態(tài)方法,可以方便地創(chuàng)建常見收集器例
具體方法與實例如下表
方法 | 返回類型 | 作用 |
---|---|---|
toList | List<T> | 把流中元素收集到List |
toSet | Set<T> | 把流中元素收集到Set |
toCollection | Collection<T> | 把流中元素收集到創(chuàng)建的集合 |
groupingBy | Map<K, List<T>> | 根據(jù)某屬性值對流分組步绸,屬性為K掺逼,結果為V |
partitioningBy | Map<Boolean, List<T>> | 根據(jù)true或false進行分區(qū) |
這里只列出了一些常用的方法.具體參考Java8 Stream API : Java Platform SE 8
Stream API 使用
中間操作
-
映射(map/flatMap)
map——接收 Lambda , 將元素轉換成其他形式或提取信息靡努。接收一個函數(shù)作為參數(shù)坪圾,該函數(shù)會被應用到每個元素上,并將其映射成一個新的元素惑朦。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
map操作會將流里的每個元素按mapper轉換后的結果添加到一個新流中.
List<String> list = Arrays.asList("1,2", "3,4"); //每次mapper操作返回一個數(shù)組,將每個數(shù)組添加到新流中,最終生成Stream<String[]> Stream<String[]> stream = list.stream().map(s -> s.split(",")); //每次mapper操作返回一個流Stream<String>,將每個流添加到新流中,最終生成Stream<Stream<String>> Stream<Stream<String>> streamStream = list.stream().map(s -> Arrays.stream(s.split(",")));
flatMap——接收一個函數(shù)作為參數(shù),將流中的每個值都換成另一個流漓概,然后把所有流連接成一個流
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
它接受一個函數(shù)mapper漾月,對流中的每一個元素,mapper會將該元素轉換為一個流Stream胃珍,然后把新生成流的每一個元素傳遞給下一個操作.
List<String> list = Arrays.asList("1,2", "3,4"); //每次mapper操作返回一個流Stream<String> 然后將流里的每個元素添加到新流中,最終生成Stream<String> Stream<String> stringStream = list.stream().flatMap(s -> Arrays.stream(s.split(",")));
flatMap 把 Stream 中的層級結構扁平化梁肿,就是將最底層元素抽出來放到一起,最終生成的新 Stream 里面都是直接的字符串觅彰。
-
排序(sort)
sorted() ——自然排序(根據(jù)流中元素實現(xiàn)的Comparable接口的compareTo()方法來排序的)
sorted(Comparator com) ——定制排序(根據(jù)特定的比較器來排序)
List<Integer> list = Arrays.asList(1,3,4,0,9,8,5); Stream<Integer> sorted = list.stream().sorted(); sorted.forEach(System.out::print); /* 輸出結果: 0134589 */
emps.stream() .sorted((x, y) -> { if (x.getAge() == y.getAge()) { return x.getName().compareTo(y.getName()); } else { return Integer.compare(x.getAge(), y.getAge()); } }).forEach(System.out::println); /* 指定比較規(guī)則,按姓名排序,姓名相同的再根據(jù)年齡排序 */
-
篩選與切片
filter : 接受Lambda,從流中排除某些元素
limit(n) : 返回流中前n個元素
skip(n) : 跳過流中前n個元素
distinct : 去掉流中重復元素(通過hashCode和equles方法判斷是否為相同對象)
filter
// 篩選出姓趙的員工 Stream<Employee> resultStream = emps.stream() .filter(employee -> employee.getName().startsWith("趙")); resultStream.forEach(System.out::println); /* 輸出結果: Employee [id=104, name=趙六, age=20, salary=7777.77] Employee [id=104, name=趙六, age=19, salary=7777.77] Employee [id=104, name=趙四, age=40, salary=7777.77] */
limit
//獲取列表前3個員工 Stream<Employee> limit = emps.stream().limit(3); limit.forEach(System.out::println); /* Employee [id=102, name=李四, age=59, salary=6666.66] Employee [id=101, name=張三, age=18, salary=9999.99] Employee [id=103, name=王五, age=28, salary=3333.33] */
skin
//去掉前3個員工 Stream<Employee> limit = emps.stream().skip(3); limit.forEach(System.out::println); /* Employee [id=104, name=趙六, age=20, salary=7777.77] Employee [id=104, name=趙六, age=19, salary=7777.77] Employee [id=104, name=趙四, age=40, salary=7777.77] Employee [id=105, name=田七, age=38, salary=5555.55] */
distinct
List<Integer> list = Arrays.asList(1, 1, 2, 2, 3, 3, 3, 3, 4, 5, 6); Stream<Integer> distinct = list.stream().distinct();//去掉重復元素 distinct.forEach(System.out::print); /* 123456 */
終止操作
- 查找與匹配
allMatch——檢查是否匹配所有元素
anyMatch——檢查是否至少匹配一個元素
noneMatch——檢查是否沒有匹配的元素
findFirst——返回第一個元素
findAny——返回當前流中的任意元素
count——返回流中元素的總個數(shù)
max——返回流中最大值
min——返回流中最小值
allMatch
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
boolean b = list.stream().allMatch(i -> i < 10);//檢查所有元素是否都小于10
//true
anyMatch
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
boolean b = list.stream().anyMatch(i -> i < 2);//檢查是否至少有一個元素小于2
//true
noneMatch
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
boolean b = list.stream().noneMatch(i -> i < 2);//檢查是否沒有一個元素小于2
//false
findFirst
//返回list第一個元素
Optional<Integer> any = list.stream().findFirst();
System.out.println(any.get());
Optional<T>
類是一個是一個容器類,代表一個值存在或不存在,原來用 null 表示一個值不存在,現(xiàn)在Optional<T>
可以更好的表達這個概念,并且可以避免空指針異常這里findFirst()查找第一個元素有可能為空,就把結果封裝成一個Optional類.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
long count = list.stream().count();//統(tǒng)計元素個數(shù)
System.out.println(count);//6
Optional<Integer> max = list.stream().max(Integer::compareTo);//最大值
System.out.println(max.get());//6
Optional<Integer> min = list.stream().min(Integer::compareTo);//最小值
System.out.println(min.get());//1
-
規(guī)約(reduce)
`reduce(T identity, BinaryOperator bo) / reduce(BinaryOperator) ——可以將流中元素按照指定的二院運算反復結合起來吩蔑,得到一個值
identity : 起始值
BinaryOperator : 二元運算
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = list.stream() .reduce(0, (x, y) -> x + y); System.out.println(sum); /* 輸出結果 55 */
這里reduce操作中,起始值為0,第一次x為0,list中第一個元素1為y 經(jīng)行(x+y)操作,然后又把(x+y)的值作為x, list中第二個元素2作為y,依次累加.最終得到一個sum值
Optional<Double> op = emps.stream() .map(Employee::getSalary) .reduce(Double::sum);//計算所有員工薪資總和 System.out.println(op.get());
這個地方由于沒有初始值,計算結果可能為空(列表為空的情況),所以就把計算結果封裝到Optional中避免空指針異常
收集(collect)
collect——將流轉換為其他形式。接收一個 Collector接口的實現(xiàn)填抬,用于給Stream中元素做匯總的方法
<R, A> R collect(Collector<? super T, A, R> collector);
//收集員工姓名到List集合
List<String> list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::print);
// 輸出: 李四張三王五趙六趙六趙六田七
//收集員工姓名到Set集合
Set<String> set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
// 輸出 : 李四張三王五趙六田七
//------------------------------------
HashSet<String> hs = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hs.forEach(System.out::println);
// 輸出 : 李四張三王五趙六田七
- 分組
groupingBy : 根據(jù)指定的元素對流中數(shù)據(jù)進行分組
//按照員工姓氏分組,這里不考慮復姓
Map<String, List<Employee>> collect = emps.stream()
.collect(Collectors
.groupingBy(emp -> String.valueOf(emp.getName().charAt(0))));
collect.forEach((k, v) -> {
System.out.println(k + ":" + v);
});
輸出結果為:
田:[Employee [id=105, name=田七, age=38, salary=5555.55]
張:[Employee [id=101, name=張三, age=18, salary=9999.99]
趙:[Employee [id=104, name=趙六, age=20, salary=7777.77],
? Employee [id=104, name=趙六, age=19, salary=7777.77],
? Employee [id=104, name=趙四, age=40, salary=7777.77]]
王:[Employee [id=103, name=王五, age=28, salary=3333.33]
李:[Employee [id=102, name=李四, age=59, salary=6666.66]
- 分區(qū)
partitioningBy : 按照給定條件對流中元素進行分區(qū)
//將員工以薪資6000.00為界限分區(qū)
Map<Boolean, List<Employee>> collect = emps.stream()
.collect(Collectors.partitioningBy(e -> e.getSalary() > 6000.00));
collect.forEach((k, v) -> {
System.out.println(k + ":" + v);
});
輸出結果為:
false:[Employee [id=103, name=王五, age=28, salary=3333.33],
? Employee [id=105, name=田七, age=38, salary=5555.55]]
true:[Employee [id=102, name=李四, age=59, salary=6666.66],
? Employee [id=101, name=張三, age=18, salary=9999.99],
? Employee [id=104, name=趙六, age=20, salary=7777.77],
? Employee [id=104, name=趙六, age=19, salary=7777.77],
? Employee [id=104, name=趙四, age=40, salary=7777.77]]
Optional 類
介紹
Optional 容器類:用于盡量避免空指針異常
方法
Optional 容器類:用于盡量避免空指針異常
Optional.of(T t) : 創(chuàng)建一個 Optional 實例
Optional.empty() : 創(chuàng)建一個空的 Optional 實例
Optional.ofNullable(T t):若 t 不為 null,創(chuàng)建 Optional 實例,否則創(chuàng)建空實例
isPresent() : 判斷是否包含值
orElse(T t) : 如果調用對象包含值,返回該值赘娄,否則返回t
orElseGet(Supplier s) :如果調用對象包含值遣臼,返回該值,否則返回 s 獲取的值
map(Function f): 如果有值對其處理揍堰,并返回處理后的Optional,否則返回 Optional.empty()
flatMap(Function mapper):與 map 類似西采,要求返回值必須是Optional
小結
Stream 是 Java8 中處理集合的關鍵抽象概念胖眷,它可以指定你希望對集合進行的操作珊搀,可以執(zhí)行非常復雜的查找、過濾和映射數(shù)據(jù)等操作劳淆。使用Stream API 對集合數(shù)據(jù)進行操作,就類似于使用 SQL 執(zhí)行的數(shù)據(jù)庫查詢曲掰。也可以使用 Stream API 來并行執(zhí)行操作。
簡而言之吊趾,Stream API 提供了一種高效且易于使用的處理數(shù)據(jù)的方式
原文鏈接 : Java8-新特性-Stream | 火堯