簡介
Stream 是一個 Collection 的增強工具,可以對集合進行各種操作,而且可以很方便的寫出并發程式,學習之前需要了解一些函式,可以看 JAVA8 Lambda運算式,常見的獲取方式就是 Collection.stream(),
操作型別
操作型別分為兩種
Intermediate(中間操作):可以多次使用,因為回傳一個Stream,比如map(mapToInt, flatMap)、filter、sorted、limit、skip,Terminal(結束操作):使用后就會結束,比如forEach、sort、collect、min、count、findFirst、anyMatch,
中間操作都是惰性的,也就是延遲的,所以會產生副作用,關于副作用會在之后的章節詳細說明,
創建
Stream 的型別有三種 IntStream,DoubleStream 和 LongStream,當然也可以使用 Stream<T>,
除了直接創建,還能通過 Collection 介面的 stream() 和 parallelStream() 創建,其中 parallelStream() 創建的流是并發、執行緒不安全且操作無序的,雖然它是并發的,但仍有可能在某些操作下變回串行的,例如 forEachOrdered,此外還需要保證資料源是執行緒安全的,
下面的代碼展示了流的創建,第二行代碼是取流中前三個元素在控制臺輸出,如果開啟最后一行注釋將會出現 java.lang.IllegalStateException 的例外,詳細錯誤資訊為 stream has already been operated upon or closed,所以創建的 Stream 僅能使用一次,
IntStream intStream = IntStream.of(1, 2, 3, 4);
intStream.limit(3).forEach(System.out::println);
//intStream.limit(1).forEach(System.out::println);
使用
JAVA8 中 Stream 介面中的操作有 filter、map、mapToInt、mapToLong、mapToDouble、flatMap、flatMapToInt、flatMapToLong、flatMapToDouble、distinct、sorted、peek、limit、skip、forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny,方法很多,沒見過的看注釋、引數和回傳值就懂了,
示例使用 List 介面 的 stream 方法創建 Stream,下面是示例所需要的資料,
//Get、Set、構造方法浪費空間,不粘貼了
public class Person {
//id
private Integer id;
//名字
private String name;
//年齡
private Integer age;
//組織
private String organization;
}
//資料初始化
List<Person> personList = new ArrayList<>();
personList.add(new Person(1, "灰原哀", 7, "帝丹小學"));
personList.add(new Person(2, "江戶川柯南", 7, "帝丹小學"));
personList.add(new Person(3, "宮野明美", 24, "黑衣組織"));
personList.add(new Person(4, "赤井秀一", 27, "FBI"));
personList.add(new Person(5, "貝爾摩德", 29, "黑衣組織"));
下面是對 Stream 操作的兩個簡單示例,
第一個將 personList 中 前 4 個、年齡為 7 的人提取出來,以組織和姓名為 key 和 value 組裝成一個新的 Map,其中 Collectors.toMap 方法最后一個引數用于解決 key 沖突,
第二個獲取 personList 中名字為安室透的第一個人,
Map<String, String> map = personList.stream().limit(4) //中間操作
.filter(person -> Objects.equals(person.getAge(), 7)) //中間操作
.collect(Collectors.toMap( // Collectors 類是隨 Stream 一起引入的,即方便又好看,作用之一是收集元素到集合
Person::getOrganization, // map 的 key
Person::getName, // map 的 value
(old, now) -> old)); // 發生沖突的解決辦法
Optional<Person> optional = personList.stream()
.filter(person->Objects.equals(person.getName(), "安室透")) //中間操作
.findFirst();
第二個方法回傳值型別是 Optional<Person>,Optional 是 JAVA8 中引入的一個容器,可以使用 get() 獲取容器中的值,但 optional 中并沒有值,所以會拋出 java.util.NoSuchElementException,為了解決這個問題可以使用 orElse(),當容器中值為空時回傳設定的默認值,除了 orElse 還有 orElseGet 和 orElseThrow,比如下面的這段代碼回傳了叫安室透的人,
// optional 在上一段代碼中產生的物件
Person person = optional.orElse(new Person(7, "安室透", 29, "日本公安"));
副作用
對流的中間操作會產生副作用,結果是拋例外和資料的錯誤,它的來源有“干擾”和“有狀態的 Lambda”,
- “干擾”就是在中間操作時修改了流的資料源,比如在
forEach(Consumer<? super T> action)中應該是消費資料,卻給資料源添加了一個資料,結果是拋出了java.util.ConcurrentModificationException例外, - “有狀態的 Lambda”,當后面操作產生的結果會被前面的操作影響時,前面操作的
Lambda就被稱作是有狀態的,比如 有狀態的 Lambda 的例子,例子中使用parallelStream()并發添加資料到parallelStorage中,結果就是parallelStorage中的資料順序不可預測,因此稱e -> { parallelStorage.add(e); return e; }是有狀態的Lambda,
總結
本文沒有深入介紹它的概念,只是簡單介紹了 Stream 的使用和 Optional 容器,對于了解應該夠了,
參考資料
為什么需要 Stream
Side Effects
Java 8 Stream 的終極技巧——Collectors 功能與操作方法詳解
Java8(3)Stream類的collect方法詳解
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/174995.html
標籤:Java
