Java 8新特性Lambda表達式Stream API用法

目錄

一、基本用法

1、基本過濾

2、基本轉換

3、基本的過濾和轉換組合

二、中間操作

1、distinct

2、sorted

3、skip/limit

4、peek

5、mapToLong/mapToInt/mapToDouble

6、flatMap

三、終端操作

1、max/min

2、count

3、allMatch/anyMatch/noneMatch

4、findFirst/findAny

5、forEach

6、toArray

7、reduce

四、容器收集器

1、toSet

2、toCollection

3、toMap

4、字符串收集器

五、 分組

1、基本用法

2、分組計數、找最大/最小元素

3、分組數值統計

4、分組內的map

5、分組結果處理(filter/sort/skip/limit)

6、分區

五、函數式數據處理思維


一、基本用法

先創建學生類

public class Student {

    private String name;

    private Integer score;

    private String grade;
}

創建學生列表

Student stu1 = Student.builder().name("張三").grade("一年級").score(92).build();
Student stu2 = Student.builder().name("李四").grade("二年級").score(89).build();
Student stu3 = Student.builder().name("王五").grade("一年級").score(90).build();
Student stu4 = Student.builder().name("趙六").grade("二年級").score(91).build();
Student stu5 = Student.builder().name("陳七").grade("一年級").score(88).build();
Student stu6 = Student.builder().name("周八").grade("二年級").score(86).build();
Student stu7 = Student.builder().name("孫九").grade("一年級").score(99).build();

List<Student>  stuList = new ArrayList<>();
stuList.add(stu1);
stuList.add(stu2);
stuList.add(stu3);
stuList.add(stu4);
stuList.add(stu5);
stuList.add(stu6);
stuList.add(stu7);

1、基本過濾

返回學生列表中90分以上的同學

List<Student> collect =
		stuList.stream()
				.filter(s -> s.getScore() > 90)
				.collect(Collectors.toList());

collect.forEach(System.out::println);

輸出結果:

Student(name=張三, score=92, grade=一年級)
Student(name=趙六, score=91, grade=二年級)
Student(name=孫九, score=99, grade=一年級)

2、基本轉換

根據學生列表返回名稱列表

List<String> nameList =
		stuList.stream()
				.map(Student::getName)
				.collect(Collectors.toList());

nameList.forEach(System.out::println);

輸出結果:

張三
李四
王五
趙六
陳七
周八
孫九

 

3、基本的過濾和轉換組合

返回90分以上的學生名稱列表

List<String> collect =
		stuList.stream()
				.filter(s -> s.getScore() > 90)
				.map(Student::getName)
				.collect(Collectors.toList());

collect.forEach(System.out::println);

輸出結果:

張三
趙六
孫九

二、中間操作

1、distinct

distinct返回一個新的Stream,過濾重複的元素,只留下唯一的元素,是否重複是根據equals方法來比較的。

List<String> strList = new ArrayList<>();

strList.add("abc");
strList.add("bcd");
strList.add("cde");
strList.add("abc");

List<String> collect =
		strList.stream()
				.distinct()
				.collect(Collectors.toList());

collect.forEach(System.out::println);

輸出結果 :

abc
bcd
cde

2、sorted

有兩個sorted方法:

Stream<T> sorted(Comparator<? super T> comparator);
Stream<T> sorted();

1)第一個方法接受一個自定義的Comparator。 根據分數排序

List<Student> collect =
		stuList.stream()
				.sorted(Comparator.comparing(Student::getSore))
				.collect(Collectors.toList());

collect.forEach(System.out::println);

輸出結果:

Student(name=周八, score=86, grade=二年級)
Student(name=陳七, score=88, grade=一年級)
Student(name=李四, score=89, grade=二年級)
Student(name=王五, score=90, grade=一年級)
Student(name=趙六, score=91, grade=二年級)
Student(name=張三, score=92, grade=一年級)
Student(name=孫九, score=99, grade=一年級)

2)第二個方法假定元素實現了Comparable接口

學生類實現Comparable接口

public class Student implements Comparable {

    private String name;

    private Integer sore;

    private String grade;

    @Override
    public int compareTo(Object obj) {

        Student stu1 = (Student) obj;

        if (stu1.getSore().equals(this.sore)){
            return 0;
        } else if (stu1.getSore() > this.sore){
            return -1;
        } else {
            return 1;
        }

    }
}

根據分數排序:

List<Student> collect1 = stuList.stream().sorted().collect(Collectors.toList());

collect1.forEach(System.out::println);

輸出結果 :

Student(name=周八, score=86, grade=二年級)
Student(name=陳七, score=88, grade=一年級)
Student(name=李四, score=89, grade=二年級)
Student(name=王五, score=90, grade=一年級)
Student(name=趙六, score=91, grade=二年級)
Student(name=張三, score=92, grade=一年級)
Student(name=孫九, score=99, grade=一年級)

3、skip/limit

Stream<T> skip(long n);
Stream<T> limit(long maxSize);

skip跳過流中的n個元素,如果流中元素不足n個,返回一個空流,limit限制流的長度爲maxSize。

比如,將學生列表按照分數從高到低排序,分數一樣的按名稱排序,返回第3名到第5名,代碼爲:

List<Student> collect =
		stuList.stream()
				.sorted( Comparator.comparing(Student::getScore)
						.reversed()
						.thenComparing(Student::getName))
				.skip(2)
				.limit(3)
				.collect(Collectors.toList());

collect.forEach(System.out::println);

輸出結果:

Student(name=王五, score=90, grade=一年級)
Student(name=趙六, score=90, grade=二年級)
Student(name=李四, score=89, grade=二年級)

skip和limit都是有狀態的中間操作。對前n個元素,skip的操作就是過濾,對後面的元素,skip就是傳遞給流水線中的下一個操作。limit的一個特點是:它不需要處理流中的所有元素,只要處理的元素個數達到maxSize,後面的元素就不需要處理了,這種可以提前結束的操作稱爲短路操作。 

4、peek

Stream<T> peek(Consumer<? super T> action);

它返回的流與之前的流是一樣的,沒有變化,但它提供了一個Consumer,會將流中的每一個元素傳給該Consumer。這個方法的主要目的是支持調試,可以使用該方法觀察在流水線中流轉的元素,比如:

List<String> collect = stuList.stream()
	.peek(System.out::println).map(Student::getName).collect(Collectors.toList());
System.out.println("====================================================");
collect.forEach(System.out::println);

輸出結果:

Student(name=張三, score=92, grade=一年級)
Student(name=李四, score=89, grade=二年級)
Student(name=王五, score=90, grade=一年級)
Student(name=趙六, score=90, grade=二年級)
Student(name=陳七, score=88, grade=一年級)
Student(name=周八, score=86, grade=二年級)
Student(name=孫九, score=99, grade=一年級)
====================================================
張三
李四
王五
趙六
陳七
周八
孫九

5、mapToLong/mapToInt/mapToDouble

map函數接受的參數是一個Function<T, R>,爲避免裝箱/拆箱,提高性能,Stream還有如下返回基本類型特定流的方法:

IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

DoubleStream/IntStream/LongStream是基本類型特定的流,有一些專門的更爲高效的方法。比如,求學生列表的分數總和,代碼爲:

int sum = stuList.stream().mapToInt(Student::getScore).sum();

6、flatMap

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

它接受一個函數mapper,對流中的每一個元素,mapper會將該元素轉換爲一個流Stream,然後把新生成流的每一個元素傳遞給下一個操作。比如:

List<String> strList = new ArrayList<>();

strList.add("abc,bcd");
strList.add("cde,def");

List<String> collect =
		strList.stream()
				.flatMap(s -> Arrays.stream(s.split(",")))
				.collect(Collectors.toList());

collect.forEach(System.out::println);

 這裏的mapper將一行字符串按空白符分隔爲了一個單詞流,Arrays.stream可以將一個數組轉換爲一個流,輸出爲:

abc
bcd
cde
def

可以看出,實際上,flatMap完成了一個1到n的映射。

三、終端操作

中間操作不觸發實際的執行,返回值是Stream,而終端操作觸發執行,返回一個具體的值,除了collect, Stream API的終端操作還有max、min、count、allMatch、anyMatch、noneMatch、findFirst、findAny、forEach、toArray、reduce等,我們逐個介紹。

1、max/min

Optional<T> max(Comparator<? super T> comparator);
Optional<T> min(Comparator<? super T> comparator);

它們返回流中的最大值/最小值,它們的返回值類型是Optional<T>,而不是T。java.util.Optional是Java 8引入的一個新類,它是一個泛型容器類,內部只有一個類型爲T的單一變量value,可能爲null,也可能不爲null。Optional有什麼用呢?它用於準確地傳遞程序的語義,它清楚地表明,其代表的值可能爲null,程序員應該進行適當的處理。

Optional<Student> max = stuList.stream().max(Comparator.comparing(Student::getScore));
Student student = max.get();
System.out.println(student);

輸出結果:

Student(name=孫九, score=99, grade=一年級)

2、count

返回流中元素的個數

long count = stuList.stream().filter(stu -> stu.getScore() > 90).count();

3、allMatch/anyMatch/noneMatch

這幾個函數都接受一個謂詞Predicate,返回一個boolean值,用於判定流中的元素是否滿足一定的條件。它們的區別是:

  • allMatch:只有在流中所有元素都滿足條件的情況下才返回true。
  • anyMatch:只要流中有一個元素滿足條件就返回true。
  • noneMatch:只有流中所有元素都不滿足條件才返回true。

如果流爲空,那麼這幾個函數的返回值都是true。比如,判斷是不是所有學生都及格了(不小於60分),代碼可以爲:

boolean b = stuList.stream().allMatch(stu -> stu.getScore() > 60);

這幾個操作都是短路操作,不一定需要處理所有元素就能得出結果,比如,對於all-Match,只要有一個元素不滿足條件,就能返回false。

4、findFirst/findAny

Optional<T> findFirst();
Optional<T> findAny();

它們的返回類型都是Optional,如果流爲空,返回Optional.empty()。findFirst返回第一個元素,而findAny返回任一元素,它們都是短路操作。隨便找一個不及格的學生,代碼可以爲:

Optional<Student> any = stuList.stream().filter(stu -> stu.getScore() < 60).findAny();

if (any.isPresent()){
	System.out.println( any.get().getName() + "拖出去,打一頓");
}

5、forEach

void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);

它們都接受一個Consumer,對流中的每一個元素,傳遞元素給Consumer。區別在於:在並行流中,forEach不保證處理的順序,而forEachOrdered會保證按照流中元素的出現順序進行處理。

stuList.stream().filter(stu -> stu.getScore() > 90).forEach(System.out::println);

6、toArray

Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);

不帶參數的toArray返回的數組類型爲Object[],這通常不是期望的結果,如果希望得到正確類型的數組,需要傳遞一個類型爲IntFunction的generator。generator接受的參數是流的元素個數,它應該返回對應大小的正確類型的數組。比如,獲取90分以上的學生數組,代碼可以爲:

Student[] students =
		stuList.stream()
				.filter(stu -> stu.getScore() > 90)
				.toArray(Student[]::new);

Arrays.stream(students).forEach(System.out::println);

輸出結果:

Student(name=張三, score=92, grade=一年級)
Student(name=孫九, score=99, grade=一年級)

7、reduce

reduce代表歸約或者叫摺疊,它是max/min/count的更爲通用的函數,將流中的元素歸約爲一個值。有三個reduce函數:

Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);
<R> R collect(Supplier<R> supplier,
                 BiConsumer<R, ? super T> accumulator,
                 BiConsumer<R, R> combiner);

reduce函數雖然更爲通用,但比較費解,難以使用,一般情況下應該優先使用其他函數。collect函數比reduce函數更爲通用、強大和易用,關於它,我們稍後再詳細介紹。

四、容器收集器

對於collect方法,前面只是演示了其最基本的應用,它還有很多強大的功能,比如與toList類似的容器收集器還有toSet、toCollection、toMap等

1、toSet

toSet的使用與toList類似,只是它可以排重,就不舉例了。toList背後的容器是ArrayList, toSet背後的容器是HashSet

2、toCollection

toCollection是一個通用的容器收集器,可以用於任何Collection接口的實現類。比如,如果希望排重但又希望保留出現的順序,可以使用LinkedHashSet, Collector可以這麼創建:

List<String> strList = new ArrayList<>();
strList.add("abc");
strList.add("bcd");
strList.add("cde");
strList.add("abc");

LinkedHashSet<String> collect = strList.stream()
        .filter(s -> s.length() > 2)
        .collect(Collectors.toCollection(LinkedHashSet::new));

collect.forEach(System.out::println);

輸出結果:

abc
bcd
cde

3、toMap

Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper);

Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction);

Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                                Function<? super T, ? extends U> valueMapper,
                                BinaryOperator<U> mergeFunction,
                                Supplier<M> mapSupplier)

 1)第一個方法示例代碼:

Map<String, Student> collect = stuList.stream()
        .collect(Collectors.toMap(Student::getName, s -> s));

System.out.println(collect);

注:鍵如果有重複的,會拋出異常的  

s->s是valueMapper,表示值就是元素本身。這個函數用得比較多,接口Function定義了一個靜態函數identity表示它。也就是說,上面的代碼可以替換爲:

Map<String, Student> collect = stuList.stream()
        .collect(Collectors.toMap(Student::getName, Function.identity()));

System.out.println(collect);

輸出結果:

{
    李四 = Student(name = 李四, score = 89, grade = 二年級),
    張三 = Student(name = 張三, score = 92, grade = 一年級), 
    王五 = Student(name = 王五, score = 90, grade = 一年級), 
    周八 = Student(name = 周八, score = 86, grade = 二年級), 
    陳七 = Student(name = 陳七, score = 88, grade = 一年級), 
    趙六 = Student(name = 趙六, score = 91, grade = 二年級), 
    孫九 = Student(name = 孫九, score = 99, grade = 一年級)
}

2)處理鍵重複問題:相比前面的toMap,多了一個mapSupplier,它是Map的工廠方法,對於前面的兩個toMap,其mapSupplier其實是HashMap::new。我們知道,HashMap是沒有任何順序的,如果希望保持元素出現的順序,可以替換爲LinkedHashMap,如果希望收集的結果排序,可以使用TreeMap。

List<String> strList = new ArrayList<>();
strList.add("abc");
strList.add("bcd");
strList.add("cde");
strList.add("abc");

Map<String, Integer> collect = 
        strList.stream().collect(Collectors.toMap(Function.identity(), s -> s.length(), (oldValue, value) -> value));
System.out.println(collect);

相比前面的toMap,它接受一個額外的參數mergeFunction,它用於處理衝突,在收集一個新元素時,如果新元素的鍵已經存在了,系統會將新元素的值與鍵對應的舊值一起傳遞給mergeFunction得到一個值,然後用這個值給鍵賦值。 

也可以進行處理:

Map<String, Integer> collect =
        strList.stream().collect(Collectors.toMap(Function.identity(), s -> s.length(), (oldValue, value) -> oldValue + value));
System.out.println(collect);

輸出結果:

{bcd=3, abc=6, cde=3}

3)第三個方法相比前面的toMap,多了一個mapSupplier,它是Map的工廠方法,對於前面的兩個toMap,其mapSupplier其實是HashMap::new。我們知道,HashMap是沒有任何順序的,如果希望保持元素出現的順序,可以替換爲LinkedHashMap,如果希望收集的結果排序,可以使用TreeMap。

Map<String, Integer> collect =
        strList.stream().collect(Collectors.toMap(Function.identity(), s -> s.length(), (oldValue, value) -> value, LinkedHashMap::new));
System.out.println(collect);

4、字符串收集器

public static Collector<CharSequence, ?, String> joining();

public static Collector<CharSequence, ?, String> joining(CharSequence delimiter);

public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
                                                             CharSequence prefix,
                                                             CharSequence suffix);

第一個就是簡單地把元素連接起來,第二個支持一個分隔符,還可以給整個結果字符串加前綴和後綴,比如:

List<String> strList = new ArrayList<>();
strList.add("abc");
strList.add("bcd");
strList.add("cde");
strList.add("abc");

String collect = strList.stream().collect(Collectors.joining(",", "[", "]"));
System.out.println(collect);

 輸出結果:

[abc,bcd,cde,abc]

五、 分組

1、基本用法

Map<String, List<Student>> collect = stuList.stream()
        .collect(Collectors.groupingBy(Student::getGrade));

System.out.println(collect);

 輸出結果:

{
    一年級=[
        Student(name=張三, score=92, grade=一年級), 
        Student(name=王五, score=90, grade=一年級), 
        Student(name=陳七, score=88, grade=一年級), 
        Student(name=孫九, score=99, grade=一年級)], 
    二年級=[
        Student(name=李四, score=89, grade=二年級), 
        Student(name=趙六, score=91, grade=二年級), 
        Student(name=周八, score=86, grade=二年級)
    ]
}

2、分組計數、找最大/最小元素

// 計數
public static <T> Collector<T, ?, Long> counting();
// 計算最小值
public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator);
// 計算最大值
public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator);

1)計數代碼示例:

Map<String, Long> collect = stuList.stream()
        .collect(Collectors.groupingBy(Student::getGrade, Collectors.counting()));

System.out.println(collect);

輸出結果:

{一年級=4, 二年級=3}

2)獲取最大值

Map<String, Optional<Student>> collect = stuList.stream()
        .collect(Collectors.groupingBy(Student::getGrade, Collectors.maxBy(Comparator.comparing(Student::getScore))));

System.out.println(collect);

 需要說明的是,這個分組收集結果是Optional<Student>,而不是Student,這是因爲maxBy處理的流可能是空流,但對我們的例子,這是不可能的。爲了直接得到Student,可以使用Collectors的另一個收集器collectingAndThen,在得到Optional<Student>後調用Optional的get方法,如下所示:

Map<String, Student> collect = stuList.stream()
        .collect(
                Collectors.groupingBy(
                        Student::getGrade,
                        Collectors.collectingAndThen(
                                Collectors.maxBy(Comparator.comparing(Student::getScore)),
                                Optional::get)));

System.out.println(collect);

輸出結果:

{
    一年級=Student(name=孫九, score=99, grade=一年級),
    二年級=Student(name=趙六, score=91, grade=二年級)
}

 關於collectingAndThen,我們稍後再進一步討論。

3、分組數值統計

除了基本的分組計數,還經常需要進行一些分組數值統計,比如求學生分數的和、平均分、最高分、最低分等、針對int、long和double類型,Collectors提供了專門的收集器,比如:

// 求平均值,double和long也有類似方法
public static <T> Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper);

// 求和,double和long也有類似方法
public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper);

// 求多種彙總信息,double和long也有類似方法
// IntSummaryStatistics包括個數、最大值、最小值、和、平均數等多種信息
public static <T> Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper);

 比如,按年級統計學生分數信息,代碼可以爲:

Map<String, IntSummaryStatistics> collect = stuList.stream()
        .collect(Collectors.groupingBy(Student::getGrade, Collectors.summarizingInt(Student::getScore)));

System.out.println(collect);

輸出結果:

{
    一年級=IntSummaryStatistics{count=4, sum=369, min=88, average=92.250000, max=99}, 
    二年級=IntSummaryStatistics{count=3, sum=266, min=86, average=88.666667, max=91}
}

4、分組內的map

對於每個分組內的元素,我們感興趣的可能不是元素本身,而是它的某部分信息。在StreamAPI中,Stream有map方法,可以將元素進行轉換,Collectors也爲分組元素提供了函數mapping,如下所示:

public static <T, U, A, R> Collector<T, ?, R> mapping(
        Function<? super T, ? extends U> mapper,Collector<? super U, A, R> downstream);

 對學生按年級分組,得到學生名稱列表,代碼可以爲:

Map<String, List<String>> collect = stuList.stream().collect(
        Collectors.groupingBy(
                Student::getGrade, Collectors.mapping(
                        Student::getName, Collectors.toList())));

System.out.println(collect);

 輸出結果:

{
    一年級=[張三, 王五, 陳七, 孫九], 
    二年級=[李四, 趙六, 周八]
}

5、分組結果處理(filter/sort/skip/limit)

對分組後的元素,我們可以計數,找最大/最小元素,計算一些數值特徵,還可以轉換(map)後再收集,那可不可以像Stream API一樣,排序(sort)、過濾(filter)、限制返回元素(skip/limit)呢?Collector沒有專門的收集器,但有一個通用的方法:

public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
                                                                Function<R,RR> finisher); 

 這個方法接受一個下游收集器downstream和一個finisher,返回一個收集器,也就是說,它在下游收集器的結果上又調用了finisher。利用這個finisher,我們可以實現多種功能,下面看一些例子。收集完再排序,可以定義如下方法:

public <T> Collector<T, ?, List<T>> collectingAndSort(Collector<T, ?, List<T>> downstream, Comparator<? super T> comparator){
    Collector<T, ?, List<T>> tListCollector = Collectors.collectingAndThen(downstream, (r) -> {
        r.sort(comparator);
        return r;
    });
    return tListCollector;
}

將學生按年級分組,分組內的學生按照分數由高到低進行排序,利用這個方法,代碼可以爲:

class DemoApplicationTests {

    @Test
    void contextLoads2() {
        //  生成學生列表
        List<Student> stuList = this.getStuList();

        Map<String, List<Student>> collect = stuList.stream().collect(
                Collectors.groupingBy(
                        Student::getGrade, this.collectingAndSort(
                                Collectors.toList(),
                                Comparator.comparing(Student::getScore).reversed())));

        System.out.println(collect);
    }

    public <T> Collector<T, ?, List<T>> collectingAndSort(Collector<T, ?, List<T>> downstream, Comparator<? super T> comparator){
        Collector<T, ?, List<T>> tListCollector = Collectors.collectingAndThen(downstream, (r) -> {
            r.sort(comparator);
            return r;
        });
        return tListCollector;
    }

    List<Student> getStuList() {
        Student stu1 = Student.builder().name("張三").grade("一年級").score(92).build();
        Student stu2 = Student.builder().name("李四").grade("二年級").score(89).build();
        Student stu3 = Student.builder().name("王五").grade("一年級").score(90).build();
        Student stu4 = Student.builder().name("趙六").grade("二年級").score(91).build();
        Student stu5 = Student.builder().name("陳七").grade("一年級").score(88).build();
        Student stu6 = Student.builder().name("周八").grade("二年級").score(86).build();
        Student stu7 = Student.builder().name("孫九").grade("一年級").score(99).build();

        List<Student>  stuList = new ArrayList<>();
        stuList.add(stu1);
        stuList.add(stu2);
        stuList.add(stu3);
        stuList.add(stu4);
        stuList.add(stu5);
        stuList.add(stu6);
        stuList.add(stu7);

        return stuList;
    }

}

6、分區

分組的一個特殊情況是分區,就是將流按true/false分爲兩個組,Collectors有專門的分區函數:

public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate);

public static <T, D, A> Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T, A, D> downstream);

 第一個函數的下游收集器爲toList(),第二個函數可以指定一個下游收集器。比如,將學生按照是否及格(大於等於60分)分爲兩組,代碼可以爲:

Map<Boolean, List<Student>> collect = stuList.stream().collect(Collectors.partitioningBy(s -> s.getScore() >= 60));
System.out.println(collect);

輸出結果:

{
    false=[], 
    true=[
        Student(name=張三, score=92, grade=一年級),
        Student(name=李四, score=89, grade=二年級), 
        Student(name=王五, score=90, grade=一年級), 
        Student(name=趙六, score=91, grade=二年級), 
        Student(name=陳七, score=88, grade=一年級), 
        Student(name=周八, score=86, grade=二年級), 
        Student(name=孫九, score=99, grade=一年級)
    ]
}

 按是否及格分組後,計算每個分組的平均分,代碼可以爲:

Map<Boolean, Double> collect = stuList.stream().collect(
        Collectors.partitioningBy(
                s -> s.getScore() >= 60, Collectors.averagingInt(Student::getScore)));

System.out.println(collect);

輸出結果:

{false=0.0, true=90.71428571428571}

五、函數式數據處理思維

可以看出,使用Stream API處理數據集合,與直接使用容器類API處理數據的思路是完全不一樣的。流定義了很多數據處理的基本函數,對於一個具體的數據處理問題,解決的主要思路就是組合利用這些基本函數,以聲明式的方式簡潔地實現期望的功能,這種思路就是函數式數據處理思維,相比直接利用容器類API的命令式思維,思考的層次更高。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章