欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Java8函數(shù)式編程之收集器的應(yīng)用

 更新時(shí)間:2023年04月14日 09:15:44   作者:CrazyDragon_King  
這篇文章主要介紹了詳解Java8函數(shù)式編程之收集器的應(yīng)用,收集器是一種通用的、從流生成復(fù)雜值的結(jié)構(gòu)??梢允褂盟鼜牧髦猩蒐ist、Set、Map等集合,需要的朋友可以參考下

收集器

收集器是一種通用的、從流生成復(fù)雜值的結(jié)構(gòu)??梢允褂盟鼜牧髦猩蒐ist、Set、Map等集合。 收集器都是在流的collect方法中調(diào)用,并且都在 Collectors類中。

java 的標(biāo)準(zhǔn)類庫(kù)提供了很多有用的收集器,當(dāng)然了,也可以自己自定義(這個(gè)對(duì)于使用者的要求很高)。

下面提供一個(gè)代碼,用于測(cè)試接下里要說(shuō)的收集器:

提供了一個(gè)簡(jiǎn)單的測(cè)試數(shù)據(jù)

學(xué)號(hào) 姓名 性別 語(yǔ)文 數(shù)學(xué) 英語(yǔ) 物理 政治 總分
09509002 節(jié)強(qiáng) 男 86 90 90 93 90
09509003 楊青 女 90 90 82 91 92
09509006 徐剛 男 78 92 83 90 87
09509111 馬力 男 77 88 99 90 88
09509001 武向麗 女 90 78 83 94 94
09509007 張文靜 女 85 90 79 94 88
09509005 徐小紅 女 78 85 88 93 92
09509009 李姝 女 92 80 75 90 88
09509004 李文華 男 68 59 70 85 90
09509008 夏婧 女 87 65 73 91 95
09509010 王洪 男 66 48 89 70 57

Student 實(shí)體類封裝數(shù)據(jù)

package com.cdragon;

public class Student implements Comparable<Student> {
    private String number;
    private String name;
    private String sex;
    private Integer chinese;
    private Integer math;
    private Integer english;
    private Integer physics;
    private Integer politics;
	//省略getter和setter方法,這個(gè)使用IDE自動(dòng)生成特別方便。
	//省略toString方法,同上。
	
	@Override
    public int compareTo(Student s) {
        return s.getNumber().compareTo(this.getNumber());
    }
}

LoadData 類加載數(shù)據(jù)到內(nèi)存

package com.cdragon;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class LoadData {

    public static List<Student> readFromFile(File file) {
        List<Student> students = new ArrayList<>();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
            String record = null;
            String header = br.readLine();
            //對(duì)于數(shù)據(jù)的第一行頭,暫時(shí)不做處理。
            while ((record = br.readLine() ) != null) {
                Student s = resolveLineToStudent(record);
                students.add(s);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return students;
    }

    private static Student resolveLineToStudent(String record) {
        String[] array = record.split("\\s+");  // \\s 和 \\s+ 還是有區(qū)別的!
        Student s = new Student();
        s.setNumber(array[0]);
        s.setName(array[1]);
        s.setSex(array[2]);
        s.setChinese(Integer.parseInt(array[3]));
        s.setMath(Integer.parseInt(array[4]));
        s.setEnglish(Integer.parseInt(array[5]));
        s.setPhysics(Integer.parseInt(array[6]));
        s.setPolitics(Integer.parseInt(array[7]));
        return s;
    }

}

收集器應(yīng)用

將流轉(zhuǎn)換成其他集合

使用收集器是可以生成其他集合的,例如生成List、Set 和 Map等,下面來(lái)分別舉例:

		//生成 List
        List<Student> studentList = students.stream().collect(Collectors.toList());
        //生成 Set
        Set<Student> studentSet = students.stream().collect(Collectors.toSet());
        //生成指定集合
        TreeSet<Student> studentTreeSet = students.stream().collect(Collectors.toCollection(TreeSet::new));
        //生成 Map 
        Map<String, Student> studentMap = students.stream().collect(Collectors.toMap(Student::getNumber, s->s));

        studentMap.forEach((no, s)->{
            System.out.println(no + "->" + s);
        });

說(shuō)明:通常的 toList() 和 toSet() 方法是不指定生成集合的具體類型,這是由系統(tǒng)來(lái)選擇最合適的類型,但是有時(shí)候我們必須返回特定的類型集合,這就用到了 toCollection() 方法,這個(gè)方法可以指定需要生成的集合的類型,這是使用方法引用進(jìn)行簡(jiǎn)化代碼:TreeSet::new

測(cè)試結(jié)果:

在這里插入圖片描述

注意:生成 Map 的方式較為復(fù)雜,因?yàn)樾枰瑫r(shí)指定鍵和值。

轉(zhuǎn)換成值

使用收集器生成一個(gè)值。

最大值和最小值

Collectors 類中的 maxBy 和 minBy 允許用戶按照某種特定順序生成一個(gè)值。它們的作用就如同它們的名字一樣,分別是尋找最大值和最小值。

我寫成一個(gè)方法,這樣調(diào)用比較方便。

/**
 * 獲取單科最高分。
 * */
public static Optional<Student> minOrMaxSubject(List<Student> students, Comparator<? super Student> comparator) {
    return students.stream().collect(Collectors.maxBy(comparator));
}

說(shuō)明:使用 maxBy 或者 minBy 必須傳入一個(gè) Comparator 對(duì)象作為參數(shù),即參數(shù)為一個(gè)比較器。

測(cè)試代碼

//這個(gè)文件的路徑應(yīng)該使用自己指定的
List<Student> students = LoadData.readFromFile(new File("src/grade.txt"));
//獲取數(shù)學(xué)最高分學(xué)生
Optional<Student> s1 = TestStream.minOrMaxSubject(students, Comparator.comparing(Student::getMath));
//獲取英語(yǔ)最高分
Optional<Student> s2 = TestStream.minOrMaxSubject(students, Comparator.comparing(Student::getEnglish));
System.out.println(s1.get());
System.out.println(s2.get());

測(cè)試結(jié)果

在這里插入圖片描述

說(shuō)明:如果想要測(cè)試最低分,只要把上面的 maxBy 改成 minBy 就行了,或者直接更進(jìn)一步,修改參數(shù)為collect里面?zhèn)魅氲暮瘮?shù),不過(guò)那樣就會(huì)顯得格外復(fù)雜,而且不止可以查最高分和最低分了。

平均值

上面看過(guò)了最大值和最小值,現(xiàn)在來(lái)看看平均值。 下面這個(gè)方法是用來(lái)求單科平均分的。

/**
 * 獲取單科平均分
 * */
 public static double averageScore(List<Student> students, ToIntFunction<? super Student> mapper) {
     return students.stream().collect(Collectors.averagingInt(mapper));
 }

測(cè)試代碼

List<Student> students = LoadData.readFromFile(new File("src/grade.txt"));
double math = TestStream.averageScore(students, Student::getMath);
System.out.println("數(shù)學(xué)的單科平均分:" + math);

測(cè)試結(jié)果

在這里插入圖片描述

數(shù)據(jù)分塊

數(shù)據(jù)分塊是指收集器將流分為兩個(gè)集合,注意分塊是只能分成兩塊。這里標(biāo)準(zhǔn)類庫(kù)提供了一個(gè)收集器 partitioningBy,它接受一個(gè)流,并將其分為兩個(gè)部分。返回的結(jié)果為一個(gè) Map,鍵只有兩種:true 或者 false,值是滿足對(duì)應(yīng)條件的集合。

例如我想知道某們成績(jī) 90分以上和一下的學(xué)生分別是哪些。

/**
 * 以特定分?jǐn)?shù)劃分不同學(xué)生,例如90分以上(含90分)和90分一下。
 * 結(jié)果是一個(gè)Map集合,只有兩個(gè)元素,true  false 個(gè)對(duì)應(yīng)一個(gè)集合。
 * */
 public static Map<Boolean, List<Student>> splitScore(List<Student> students, Predicate<? super Student> predicate) {
     return students.stream().collect(Collectors.partitioningBy(predicate));
 }

說(shuō)明:partitioningBy的參數(shù)為一個(gè) Predicate 對(duì)象,這個(gè)和過(guò)濾器的很相似,功能上可以對(duì)比學(xué)習(xí)。

測(cè)試代碼

//數(shù)學(xué)成績(jī)以90分來(lái)劃分學(xué)生
Map<Boolean, List<Student>> booleanListMap = TestStream.splitScore(students, stu->stu.getMath()>=90);
booleanListMap.forEach((bool, list)->{
    System.out.println("數(shù)學(xué)成績(jī)大于90分:" + bool);
    list.forEach(System.out::println);
    System.out.println("========================");
});

測(cè)試結(jié)果

在這里插入圖片描述

數(shù)據(jù)分組

數(shù)據(jù)分組是一種更為自然的分割數(shù)據(jù)操作,與將數(shù)據(jù)分成true和false兩部分不同,可以使用任意值對(duì)數(shù)據(jù)分組。比如使用性別對(duì)學(xué)生進(jìn)行分組。這很像SQL中的 groupBy 操作。

/**
* 數(shù)據(jù)分組
* 這里以性別來(lái)分組
* */
public static Map<String, List<Student>> groupBy(List<Student> students) {
    return students.stream().collect(Collectors.groupingBy(Student::getSex));
}

測(cè)試代碼

Map<String, List<Student>> stringListMap = TestStream.groupBy(students);
stringListMap.forEach((sex, list)->{
    System.out.println("性別:" + sex);
    list.forEach(System.out::println);
    System.out.println("============");
});

測(cè)試結(jié)果

在這里插入圖片描述

字符串

收集流中的數(shù)據(jù)最后生成一個(gè)字符串,這是一個(gè)很平常的操作。 例如一個(gè)所有學(xué)生的姓名列表,使用傳統(tǒng)的迭代列表操作代碼如下:

 /**
  * 獲取所有學(xué)生姓名的字符串
  * 傳統(tǒng)的迭代操作
  * 格式如下:[張三,李四]
  * */
 public static String nameStr1(List<Student> students) {
     StringBuilder builder = new StringBuilder("[");
     for (Student stu : students) {
         if (builder.length() > 1){
             builder.append(",");
         }
         String name = stu.getName();
         builder.append(name);
     }
     builder.append("]");
     return builder.toString();
 }

然后是使用收集器進(jìn)行操作,代碼如下: 這里我添加一些細(xì)節(jié)處理,學(xué)生的排名按照學(xué)生的總成績(jī)從高到底排列,這是很符合習(xí)慣的。

/**
 * 獲取所有學(xué)生姓名的字符串
 * 函數(shù)式方法
 * 格式如下:[張三,李四]
 *
 * 注意,他只能連接字符串,所有這里使用 map 操作,將 Student 轉(zhuǎn)成 String(學(xué)生姓名)
 * */
public static String nameStr2(List<Student> students) {
    return students.stream()
            .sorted(Comparator.comparing(s -> {
                return s.getChinese() + s.getMath() + s.getEnglish()
                        + s.getPhysics() + s.getPolitics();
            }, Comparator.reverseOrder()))  //(sum1, sum2)-> sum2.compareTo(sum1)
            .map(Student::getName)
            .collect(Collectors.joining(",","[","]"));
}

說(shuō)明:這里的 sorted 需要傳入一個(gè) Comparator 對(duì)象,但是可以使用靜態(tài)方法 Compring 進(jìn)行簡(jiǎn)化,但是它只是指定需要排序的標(biāo)準(zhǔn),并沒(méi)有說(shuō)是從小到大還是從大到小,后來(lái)才發(fā)現(xiàn),這個(gè)是默認(rèn)的:大小到大排序。但是我需要的是使用從大到小,然后發(fā)現(xiàn)原來(lái) compring 還有重載方法,具有兩個(gè)參數(shù),另一個(gè)參數(shù)是可以指定大小順序的,所以第二個(gè)參數(shù)我傳入了一個(gè) Lambda 表達(dá)式:

(sum1, sum2)-> sum2.compareTo(sum1)

但是如果這樣使用的話,還不如直接使用 Lambda 表達(dá)式創(chuàng)建 Comparator 對(duì)象方便呢,后來(lái)發(fā)現(xiàn)這個(gè) IDE 比較智能,它指出這句代碼,可以被替換為:

Comparator.reverseOrder();// 看意思就知道是 反序的意思。

這樣看來(lái)使用 Comparator 靜態(tài)的 comparing 方法還是比直接創(chuàng)建 Comparator 對(duì)象簡(jiǎn)單一些。

注意:如果不需要排序的話,就只有一個(gè)map方法和join方法了。這個(gè)map方法的作用是映射(我一開始把它和map集合總是搞混了),將Student對(duì)象映射為name字符串,然后使用 join 方法進(jìn)行連接。

組合收集器

收集器還可以組合起來(lái)使用,這個(gè)和 SQL 感覺(jué)更像了,幾乎具有函數(shù)式編程的語(yǔ)言,都有SQL那種處理數(shù)據(jù)的方式,例如最大值、最小值和分組等操作。 考慮對(duì)于學(xué)生按照性別分組,然后再分別統(tǒng)計(jì)男女生的人數(shù)。(這個(gè)在 SQL 里面也是一個(gè)基本的練習(xí)。)

/**
 * 組合收集器
 * 這里以性別來(lái)分組,再分別計(jì)數(shù)
 * */
public static Map<String, Long> combination(List<Student> students) {
    return students.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.counting()));
}

測(cè)試代碼

Map<String, Long> stringLongMap = TestStream.combination(students);
stringLongMap.forEach((sex, count)->{
    System.out.println("性別:" + sex + ", 人數(shù):" + count);
});

測(cè)試結(jié)果

在這里插入圖片描述

使用流的其他操作

對(duì)于流的使用,應(yīng)該達(dá)到一個(gè)較為熟練的地步,但是由于沒(méi)有什么機(jī)會(huì)實(shí)踐,還是比較陌生。下面介紹幾個(gè)我寫的方法,來(lái)看看流的操作:

//通過(guò)過(guò)濾器選擇特定的學(xué)生,過(guò)濾器用于過(guò)濾,然后選擇第一個(gè)學(xué)生。
//這里應(yīng)該加一個(gè)排序操作比較好。
public static Optional<Student> selectStudent(List<Student> students, Predicate<?  super Student> pre) {
        return students.stream().filter(pre).findFirst();
    }

public static List<Student> orderBy(List<Student> students, Comparator<? super Student> comparator) {
    if (comparator != null){
        return students.stream().sorted(comparator).collect(Collectors.toList());
    } else {
        return students.stream().sorted().collect(Collectors.toList());
    }
}

//獲取一列數(shù)據(jù)。不是一行學(xué)生記錄,是一列。
public static List<?> getAColumn(List<Student> students, Function<? super Student, ?> mapper) {
    return students.stream().map(mapper).collect(Collectors.toList());
}

/**
 * 獲取所有學(xué)生的總分和學(xué)號(hào)
 * */
public static Map<String, Integer> getSum(List<Student> students) {
    return students.stream().collect(Collectors.toMap(Student::getNumber, stu->{
        return stu.getChinese() + stu.getEnglish() + stu.getMath() + stu.getPhysics() + stu.getPolitics();
    }));
}


/**
 * peek 和 forEach 的區(qū)別
 * peek 是一個(gè)中間操作,forEach 是一個(gè)終結(jié)操作。
 *
 * 假如實(shí)現(xiàn)一個(gè)功能:每個(gè)學(xué)生的某門科目分?jǐn)?shù)進(jìn)行修改。
 *
 * peek 操作后得到的仍然是一個(gè) stream,此時(shí)可以進(jìn)一步操作,
 * 但是 forEach 是終結(jié)操作,操作結(jié)束,流就結(jié)束了,如果需要進(jìn)一步處理,
 * 必須再次進(jìn)行得到流的操作。
 * */

public static List<Student> addScore1(List<Student> students, Consumer<? super Student> action) {
    return students.stream().peek(action).collect(Collectors.toList());
}

public static void addScore2(List<Student> students, Consumer<? super Student> action) {
    students.stream().forEach(action);
}

//指定返回類型為 LinkedList,這時(shí)一個(gè)測(cè)試,并不是說(shuō)需要這樣寫。
//多數(shù)情況下,我們還是應(yīng)該使用 ArrayList
public static List<Student> addScore3(List<Student> students, Consumer<? super Student> action) {
    return students.stream().peek(action).collect(Collectors.toCollection(LinkedList::new));
}

對(duì)于其中的幾個(gè)進(jìn)行測(cè)試(不是全部方法,如果感興趣,可以自己嘗試。):

//對(duì)于學(xué)生進(jìn)行排序,參數(shù)為一個(gè)比較器,參數(shù)為空的話,使用默認(rèn)的 sorted 排序。
//測(cè)試代碼 按照學(xué)號(hào)排序(默認(rèn)從小到大)
TestStream.orderBy(students,Comparator.comparing(Student::getNumber)).forEach(System.out::println);

//按照學(xué)號(hào)排序(從大到?。?
TestStream.orderBy(students,Comparator.comparing(Student::getNumber, Comparator.reverseOrder())).forEach(System.out::println);

//使用默認(rèn)的排序
TestStream.orderBy(students).forEach(System.out::println);

//獲取一列學(xué)生的記錄,例如這里是英語(yǔ)成績(jī),這里返回值我使用通配符應(yīng)該沒(méi)有錯(cuò)吧
//因?yàn)榉祷財(cái)?shù)據(jù)可能為 String 也可能是 Integer
TestStream.getAColumn(students,Student::getEnglish).forEach(System.out::println);

//測(cè)試學(xué)生的總分
TestStream.getSum(students).forEach((no, stu)->{
    System.out.println(no + " -> " + stu);
});

總結(jié)

雖然這個(gè)收集器(也可以說(shuō)Java的函數(shù)式編程)有的使用起來(lái)感覺(jué)很簡(jiǎn)單、簡(jiǎn)潔(當(dāng)然了它的目的也是如此),但是內(nèi)部實(shí)現(xiàn)看著還是感覺(jué)無(wú)從下手,大量使用了泛型、通配符上下限這些方面的知識(shí),這對(duì)于使用來(lái)說(shuō)可以忽視,但是如果想要深入了解的話,還是很有難度的,我就先看到這里吧,消化消化知識(shí)再說(shuō),哈哈!

到此這篇關(guān)于詳解Java8函數(shù)式編程之收集器的應(yīng)用的文章就介紹到這了,更多相關(guān)Java函數(shù)式編程收集器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot2.3.0配置JPA的實(shí)現(xiàn)示例

    SpringBoot2.3.0配置JPA的實(shí)現(xiàn)示例

    這篇文章主要介紹了SpringBoot2.3.0配置JPA的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Spring security中的授權(quán)

    Spring security中的授權(quán)

    本篇為大家?guī)?lái)Spring security的授權(quán),首先要理解一些概念,有關(guān)于:權(quán)限、角色、安全上下文、訪問(wèn)控制表達(dá)式、方法級(jí)安全性、訪問(wèn)決策管理器,這篇文章主要介紹了Spring security中的授權(quán),需要的朋友可以參考下
    2024-01-01
  • ArrayList集合初始化及擴(kuò)容方式

    ArrayList集合初始化及擴(kuò)容方式

    這篇文章主要介紹了關(guān)于ArrayList集合初始化及擴(kuò)容方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 深入理解Struts2國(guó)際化信息機(jī)制

    深入理解Struts2國(guó)際化信息機(jī)制

    本篇文章主要介紹了深入理解Struts2國(guó)際化信息機(jī)制,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • springboot項(xiàng)目數(shù)據(jù)庫(kù)密碼如何加密

    springboot項(xiàng)目數(shù)據(jù)庫(kù)密碼如何加密

    在我們?nèi)粘i_發(fā)中,我們可能很隨意把數(shù)據(jù)庫(kù)密碼直接明文暴露在配置文件中,今天就來(lái)聊聊在springboot項(xiàng)目中如何對(duì)數(shù)據(jù)庫(kù)密碼進(jìn)行加密,感興趣的可以了解一下
    2021-07-07
  • 關(guān)于ArrayList初始化容量的問(wèn)題

    關(guān)于ArrayList初始化容量的問(wèn)題

    這篇文章主要介紹了關(guān)于ArrayList初始化容量的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Mybatis防止sql注入的實(shí)例

    Mybatis防止sql注入的實(shí)例

    本文通過(guò)實(shí)例給大家介紹了Mybatis防止sql注入的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-06-06
  • 強(qiáng)烈推薦這些提升代碼效率的IDEA使用技巧

    強(qiáng)烈推薦這些提升代碼效率的IDEA使用技巧

    在平常的開發(fā)中,發(fā)現(xiàn)一些同事對(duì)Idea 使用的不是很熟練,僅僅用來(lái)編輯,編譯,不能很好的發(fā)揮Idea 的神奇.整理了下我平常用的一些技巧,希望你能從中學(xué)習(xí)到一些.需要的朋友可以參考下
    2021-05-05
  • SpringCloud中的斷路器(Hystrix)和斷路器監(jiān)控(Dashboard)

    SpringCloud中的斷路器(Hystrix)和斷路器監(jiān)控(Dashboard)

    本篇主要介紹的是SpringCloud中的斷路器(Hystrix)和斷路器指標(biāo)看板(Dashboard)的相關(guān)使用知識(shí),需要的朋友可以參考下
    2019-06-06
  • Spring中@Import的各種用法以及ImportAware接口詳解

    Spring中@Import的各種用法以及ImportAware接口詳解

    這篇文章主要介紹了Spring中@Import的各種用法以及ImportAware接口詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10

最新評(píng)論