Java™ 教程(聚合操做)

聚合操做

你使用集合作什麼?你不可能簡單地將對象存儲在集合中並將它們留在那裏,在大多數狀況下,使用集合檢索存儲在其中的項。java

再次考慮Lambda表達式小節中描述的場景,假設你正在建立一個社交網絡應用程序,你但願建立一個功能,使管理員可以對知足某些條件的社交網絡應用程序的成員執行任何類型的操做,例如發送消息。git

如前所述,假設這個社交網絡應用程序的成員由如下Person類表示:github

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;
    
    // ...

    public int getAge() {
        // ...
    }

    public String getName() {
        // ...
    }
}

下面的示例使用for-each循環打印集合roster中包含的全部成員的名稱:segmentfault

for (Person p : roster) {
    System.out.println(p.getName());
}

下面的示例打印集合roster中包含的全部成員,但使用集合操做forEach數組

roster
    .stream()
    .forEach(e -> System.out.println(e.getName());

儘管在本例中,使用聚合操做的版本比使用for-each循環的版本要長,可是你將看到,對於更復雜的任務,使用批量數據操做的版本將更加簡潔。網絡

在示例BulkDataOperationsExamples中找到本節中描述的代碼摘錄。數據結構

管道和流

管道是聚合操做的序列,下面的示例打印集合roster中包含的男性成員,其中包含由聚合操做filterforEach組成的管道:函數

roster
    .stream()
    .filter(e -> e.getGender() == Person.Sex.MALE)
    .forEach(e -> System.out.println(e.getName()));

將此示例與下面的示例進行比較,下面的示例打印集合roster中包含的男性成員,並使用for-each循環:code

for (Person p : roster) {
    if (p.getGender() == Person.Sex.MALE) {
        System.out.println(p.getName());
    }
}

管道包含如下組件:對象

  • 源:能夠是集合、數組、生成器函數或I/O通道,在本例中,源是集合roster
  • 零或多箇中間操做,中間操做(如filter)生成一個新的流。

    流是元素的序列,與集合不一樣,它不是存儲元素的數據結構,相反,流經過管道攜帶來自源的值,這個示例經過調用方法stream從集合roster建立一個流。

    filter操做返回一個新的流,其中包含與其predicate匹配的元素(此操做的參數),在本例中,predicate是lambda表達式e -> e.getGender() == Person.Sex.MALE。若是對象egender字段的值爲Person.Sex.MALE,則返回布爾值true,所以,本例中的filter操做返回一個包含集合roster中全部男性成員的流。
  • 一個終端操做,終端操做(如forEach)生成非流結果,如原始值(如雙精度值)、集合,或者在forEach的狀況下,根本沒有值。在本例中,forEach操做的參數是lambda表達式e -> System.out.println(e.getName()),調用對象e上的getName方法(Java運行時和編譯器推斷對象e的類型是Person)。

下面的示例計算集合roster中包含的全部男性成員的平均年齡,其中管道由聚合操做filtermapToIntaverage組成:

double average = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .mapToInt(Person::getAge)
    .average()
    .getAsDouble();

mapToInt操做返回一個類型爲IntStream的新流(這是一個只包含整數值的流),該操做將其參數中指定的函數應用於特定流中的每一個元素,在這個例子中,函數是Person::getAge,它是一個方法引用,返回成員的年齡(或者,你可使用lambda表達式e -> e. getage())。所以,本例中的mapToInt操做返回一個流,其中包含集合roster中全部男性成員的年齡。

average操做計算類型IntStream中包含的元素的平均值,它返回一個OptionalDouble類型的對象,若是流不包含元素,則average操做返回OptionalDouble的空實例,調用getAsDouble方法將拋出NoSuchElementException。JDK包含許多終端操做,好比average,經過組合流的內容返回一個值,這些操做稱爲概括操做。

聚合操做和迭代器之間的區別

forEach這樣的聚合操做看起來像迭代器,然而,它們有幾個根本的區別:

  • 它們使用內部迭代:聚合操做不包含像next這樣的方法來指示它們處理集合的下一個元素,使用內部委託,應用程序決定迭代什麼集合,可是JDK決定如何迭代集合,使用外部迭代,你的應用程序將肯定它迭代哪些集合以及如何迭代。然而,外部迭代只能按順序迭代集合的元素,內部迭代沒有這種限制,它能夠更容易地利用並行計算,這涉及到將一個問題劃分爲子問題,同時解決這些問題,而後將子問題的解的結果組合起來。
  • 它們處理來自流的元素:聚合操做處理流中的元素,而不是直接從集合中聚合,所以,它們也稱爲流操做。
  • 它們支持將行爲做爲參數:能夠將lambda表達式指定爲大多數聚合操做的參數,這使你可以自定義特定聚合操做的行爲。

上一篇:SortedMap接口

相關文章
相關標籤/搜索