轉載連結:https://www.jianshu.com/p/3e1501fd33a9
1. 簡介
Stream API 為Java 處理資料提供了一種強大的替代方法。在這篇文章中,我們主要介紹Stream API 中一個經常被錯誤了解的方法
peek()
2. 樣例
假設我們有一個Person的流,我們想将Person的名字,年齡等資訊列印到控制台。
peek()
方法的唯一參數是
Consumer<? super T>
看上去這就是我們想要的(不是唯一可以滿足我們的):
Person 定義:
public class Person {
private String name;
private int age;
}
樣例 1:
@Test
public void test_printInfoButNot() {
Person a = new Person("a", 18);
Person b = new Person("b", 23);
Person c = new Person("c", 34);
Stream<Person> persons = Stream.of(a, b, c);
persons.peek(System.out::println);
}
非常的遺憾,上面的代碼沒有任何輸出。為了了解上面的代碼為什麼沒有輸出,我們快速的回顧一下與Stream生命周期相關的知識。
3. 中間操作 vs. 終止操作
流由三部分構成:資料源,一個或多個中間操作,一個或多個終止操作。
資料源:為流提供資料。
中間操作:依次擷取資料并處理資料。所有的中間操作都是“懶操作”,也就是說在流開始工作之前,中間操作對流中的資料沒有任何影響。
終止操作:流生命周期的結束,它可以觸發流開始工作,中間操作也就會影響流中的資料。
4. 使用 peek()
在我們的例子中,
peek()
不起作用的原因是
peek()
是一個中間操作,而且我們沒有提供一個終止操作。我們可以使用
forEach
(和
peek()
參數一樣)來實作列印使用者名和年齡的目标:
樣例 2:
@Test
public void test_printInfo() {
Person a = new Person("a", 18);
Person b = new Person("b", 23);
Person c = new Person("c", 34);
Stream<Person> persons = Stream.of(a, b, c);
persons.forEach(System.out::println);
}
輸出結果如下:
Person{name='a', age=18}
Person{name='b', age=23}
Person{name='c', age=34}
按照Java團隊的說法,
peek()
方法存在的主要目的是用調試,通過
peek()
方法可以看到流中的資料經過每個處理點時的狀态。一個簡單的樣例如下:
樣例 3:
@Test
public void test_peekDebug() {
Person a = new Person("a", 18);
Person b = new Person("b", 23);
Person c = new Person("c", 34);
Stream<Person> persons = Stream.of(a, b, c);
persons.filter(person -> person.getAge() < 30)
.peek(person -> System.out.println("filter " + person))
.map(person -> new Person(person.getName() + " map", person.getAge()))
.peek(person -> System.out.println("map " + person))
.collect(Collectors.toList());
}
樣例 3 輸出結果如下:
filter Person{name='a', age=18}
map Person{name='a map', age=18}
filter Person{name='b', age=23}
map Person{name='b map', age=23}
通過輸出結果來看,
peek()
方法确實能夠幫助我們觀察傳遞給每個操作的資料。
除去用于調試,
peek()
在需要修改元素内部狀态的場景也非常有用,比如我們想将所有Person的名字修改為大寫,當然也可以使用
map()
和
flatMap
實作,但是相比來說
peek()
更加友善,因為我們并不想替代流中的資料。
樣例 4 :
@Test
public void test_modifyInnerState() {
Person a = new Person("a", 18);
Person b = new Person("b", 23);
Person c = new Person("c", 34);
Stream<Person> persons = Stream.of(a, b, c);
persons.peek(person -> person.setName(person.getName().toUpperCase()))
.forEach(System.out::println);
}
樣例 4 的輸出結果如下:
Person{name='A', age=18}
Person{name='B', age=23}
Person{name='C', age=34}