在現在的企業級開發中, 隨着新技術的迭代, 越來越多的公司開始使用Java8的新特性去簡化開發, 因此Java8非常值得我們學習.
在學習Java8的時候, 我們需要了解一下Java8都有哪些主要的新特性
- 函數式接口
- Lambda表達式
- Stream集合的流式編程
- 新時間日期API等
而在企業開發中, 主要用到的便是Lambda表達式和Stream流 , 而在下面,我們便主要的去學習這兩方面的知識
Lambda表達式
Lambda表達式本質上是一個匿名函數, 可以簡單理解爲一個可以將代碼像數據一樣傳遞的代碼
特點: 簡潔, 靈活
基本語法
在寫lambda表達式時, 只需關注函數和方法體即可
參數: 寫在小括號中
方法體: 寫在大括號內
->: lambda表達式運算符, 分隔參數和方法體
注意:
- lambda主要用於簡化函數式接口, 而函數式接口在Java中是指: 有且僅有一個抽象方法的接口
- 只有確保接口中有且僅有一個抽象方法,Java中的Lambda才能順利地進行推導
舉例:
定義了三個接口, 利用lambda去實現相關功能
/**
* Author TimePause
* Create 2020-05-03 22:03
*/
public interface SingleReturnMultipleParameter {
int test(int a,int b);
}
/**
* Author TimePause
* Create 2020-05-03 22:04
*/
public interface NoneReturnParmarter {
void test();
}
/**
* Author TimePause
* Create 2020-05-03 22:26
*/
public interface SingleReturnSingleParameter {
int test(int x);
}
/**
* lambda表達式: 對接口進行簡潔的實現
*
* @author TimePause
* @create 2020-05-03 22:05
*/
public class TestLambda {
public static void main(String[] args) {
//測試lambda無返回值無參
NoneReturnParmarter lambda1= ()-> {
System.out.println("lambda-NoneReturnParmarter");
};
lambda1.test();
//測試lambda單個返回值多個參數
SingleReturnMultipleParameter lambda2=(int a,int b)->{
return a + b;
};
System.out.println(lambda2.test(1, 2));
//測試lambda單個參數單個返回值
SingleReturnSingleParameter lambda3=(int x)->{
return x*x;
};
System.out.println(lambda3.test(3));
//測試lambda單個/多個參數省略參數類型
SingleReturnMultipleParameter lambda5=(a,b)->{
return a + b;
};
System.out.println(lambda5.test(1, 2));
//測試lambda單個參數省略小括號(兩個無法省略)
SingleReturnSingleParameter lambda6= x->{return x*x; };
/**
* 注意:
* 1.如果方法體中代碼只有一行,那麼大括號可以省略
* 2.如果唯一的一行代碼是返回語句,那麼在大括號省略的同時, 返回值也必須省略
*/
NoneReturnParmarter lambda7= ()-> System.out.println("lambda-NoneReturnParmarter");
SingleReturnSingleParameter lambda8= x-> x*x;
}
}
lambda表達式的函數引用
- 將一個接口的實現,引用到一個已存在的方法上
- 注意: 要引用的方法的參數和返回值要和接口中定義的一致
舉例:
使用上面實現過的接口來實現引用
/**
* Author TimePause
* Create 2020-05-03 22:03
*/
public interface SingleReturnMultipleParameter {
int test(int a,int b);
}
/**
* 測試lambda表達式的引用
*
* @author TimePause
* @create 2020-05-04 22:11
*/
public class TestLambda2 {
public static void main(String[] args) {
//測試lambda表達式的引用: 類名::方法名
SingleReturnMultipleParameter lambda=TestLambda2::getAbs;
lambda.test(2, 3);
//相當於下面的方式
SingleReturnMultipleParameter lambda1 = (a, b) -> TestLambda2.getAbs(a, b);
System.out.println(lambda1.test(2, 3));
//測試對非靜態方法的引用
NoneReturnParmarter lambda2=new TestLambda2()::show;
lambda2.test();
}
/**
* 引用注意:
* 1.把私有修飾符去掉以後可在其他類中通過 類名::方法名引用其他方法
* 2.如果是靜態方法引用方式爲: 類名::方法名
* 如果是非靜態方法引用方式爲: 對象::方法名
*/
static int getAbs(int a,int b){
return a>b?a-b:b-a;
}
public void show(){
System.out.println("如果是非靜態方法引用方式爲: 對象::方法名");
}
}
lambda表達式對構造方法的引用
lambda在引用時,會根據接口類型不同自動適配是帶參還是無參的構造方法!!!
/**
* 測試lambda表達式對構造方法的引用
*
* @author TimePause
* @create 2020-05-04 22:38
*/
public class TestLambda3 {
public static void main(String[] args) {
//lambda在引用時,會根據接口類型不同自動適配是帶參還是無參的構造方法!!!
PersonMake lambda1=Person::new;
PersonMake2 lambda2 = Person::new;
}
}
//定義一個實體類兩個接口, 分別測試lambda對帶參和無參方法的引用
class Person{
String name;
int age;
Person(){}
Person(String name,int age){
this.name = name;
this.age = age;
}
}
interface PersonMake{
Person getPerson();
}
interface PersonMake2{
Person getPerson(String name,int age);
}
lambda表達式對get(),set()方法的引用
引用時需要根據接口進行適配
public class TestLambda3 {
public static void main(String[] args) {
//對get方法的引用
getName lambda4=Person::getName;
//對set方法的引用
setName lambda5=new Person()::setName;
}
}
class Person{
String name;
int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
interface getName{
String get(Person p);
}
interface setName{
void set(String name);
}
lambda簡單應用1
/**
* 測試lambda表達式實現數組的降序排序
*
* @author TimePause
* @create 2020-05-05 9:24
*/
public class TestLambda4 {
public static void main(String[] args) {
Integer arr[] = {1, 9, 8, 3, 5, 6};
//ctrl+f12=>查看當前類下所有方法
Arrays.sort(arr);//這是正序
System.out.println(Arrays.toString(arr));
Arrays.sort(arr,(x,y)-> y-x);//這是倒序
System.out.println(Arrays.toString(arr));
}
}
簡單應用2
將上面的TestLambda3 代碼進行修改,可以測試lambda對對象數組的某個屬性進行排序的情況
public class TestLambda3 {
public static void main(String[] args) {
Person[] persons=new Person[5];
persons[0] = new Person("小1", 33);
persons[1] = new Person("小2", 21);
persons[2] = new Person("小3", 25);
persons[3] = new Person("小4", 28);
persons[4] = new Person("小5", 19);
//利用lambda對其按照年齡進行正序排序
Arrays.sort(persons,(p1,p2)->p1.getAge()-p2.getAge());
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i]);
}
}
}
//定義一個實體類兩個接口, 分別測試lambda對帶參和無參方法的引用
class Person{
String name;
int age;
Person(){}
Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Stream(集合的流式編程)
在對集合中的元素進行操作的時候, 有時候可能會用到其他結果, 在這個過程中使用集合的流式編程可以簡化代碼量,
將數據源中的數據讀取到一個流中, 可以對這個流進行操作(刪除, 過濾, 映射…)
介紹
Stream流是對集合操作的增強, 它不是一種數據結構,也不負責存儲. 更像一個迭代器,且單向遍歷不可循環
實現步驟
- 獲取數據源, 讀取到流中
- 對流中的數據進行操作(中間操作)
- 對流中的數據進行整合處理(最終操作)
注意:
- 幾乎所有中間操作和最終操作的參數和方法都是函數式接口,
- 因此使用集合的流式編程進行簡化處理的前提是熟練使用lambda表達式
數據源的獲取
數據源就是數據的來源, 從數據源中讀取到流中
需要注意的是對流中數據的操作(刪除, 映射, 過濾…)是不會影響數據源的數據的
/**
* 測試Stream流式編程獲取數據源
*
* @author TimePause
* @create 2020-05-05 11:34
*/
public class TestStream {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
/**
* 數據源的獲取
* 1.stream獲取的數據源是串行的
* 2.parallelStream獲取的數據源是並行的, 且該方法封裝了多線程對數據的操作, 效率更高
*/
list.stream();
list.parallelStream();
//Stream獲取數組元素
int[] arr={1,9,8,0,2,3};
IntStream stream = Arrays.stream(arr);
}
}
最終操作
最終操作指的是將流中的數據整合在一起, 放入一個集合, 也可以直接對流中的數據進行遍歷統計, 提取出我們想要的信息
注意: 在使用最終操作後會關閉這個流並銷燬流中的數據, 如果再次使用這個已關閉的流則會出現異常
/**
* 測試最終操作
*
* @author TimePause
* @create 2020-05-05 12:09
*/
public class TestStream2 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
Collections.addAll(list, 5, 10, 15, 20, 25);
/**
* 1.collect:
* 通過在collect()方法內調用Collectors的其他方法,將流中的數據轉化成其他類型的數據
*/
//將最終結果轉成list
List<Integer> collect = list.stream().collect(Collectors.toList());
System.out.println(collect);
//將最終結果轉成set
Set<Integer> set = list.stream().collect(Collectors.toSet());
System.out.println(set);
//將最終結果轉成map
Map<Integer, Integer> map = list.stream().collect(Collectors.toMap(e -> e / 5, e -> e));
System.out.println(map);
/**
* 2.reduce:將流中的所有元素帶入這個方法中進行運算
* 最終的運算結果是一個optional類型的數據,需要調用get()方法獲取其中的數據
*/
int res = list.stream().reduce((e1, e2) -> e1 + e2).get();
System.out.println(res);
/**
* 3.count:統計流中的數據的個數
*/
long count = list.stream().count();
System.out.println(count);
/**
* 4.foreach:遍歷流中的元素
*/
list.stream().forEach(System.out::println);
/**
* 5.max&min: 獲取流中的最大值和最小值
*/
Integer max = list.stream().max(Integer::compareTo).get();
System.out.println("集合中的最大值是: "+max);
Integer min = list.stream().min(Integer::compareTo).get();
System.out.println("集合中的最小值是: "+min);
/**
* 6.allMatch & anyMatch & noneMatch=>matching
* allMatch: 只有流中所有元素都滿足匹配規則,才返回true
* anyMatch: 只要有任意一個元素滿足匹配規則,就返回true
* noneMatch: 只有流中所有的元素都不滿足匹配規則,才返回true
*/
boolean allMatch = list.stream().allMatch(e -> e > 20);
System.out.println(allMatch);
boolean anyMatch = list.stream().anyMatch(e -> e > 20);
System.out.println(anyMatch);
boolean noneMatch = list.stream().noneMatch(e -> e %5 !=0);
System.out.println(noneMatch);
/**
* 7.find: 查找流中的第一個元素
* findFirst()和findAny()在絕大多數情況下作用是一樣的, 只有在多線程的環境下才可能不同
*/
Integer findFirst = list.parallelStream().findFirst().get();
Integer findAny = list.parallelStream().findAny().get();
System.out.println(findFirst +"---"+findAny);
/**
* 注意事項:在使用最終操作後會關閉這個流並銷燬流中的數據, 如果再次使用這個已關閉的流則會出現異常
* 異常打印如下:
* Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
* at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
* at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
*/
Stream<Integer> stream = list.stream();
long count1 = stream.count();
System.out.println(count1);
Integer integer = stream.findFirst().get();
System.out.println(integer);
}
}
中間操作
中間操作指的是對流中的數據做各式各樣的處理, 中間操作可以是連續的操作, 每次操作都返回一個Stream對象, 直到最終操作的執行
/**
* 測試Stream的中間操作(對數組,集合進行操作)
*
* @author TimePause
* @create 2020-05-05 15:45
*/
public class TestStream3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "hello", "world", "hello", "TimePause", "csdn");
/**
* 1.filter:過濾流中符合條件的元素
*/
list.stream().filter(e->e.length()>5).forEach(System.out::println);
/**
* 2.distinct: 去除集合中重複的元素
* 這個方法沒有參數, 去重規則與hashset相同(會比較hashcode,如果hashcode相同會比較equals,equals相同則認爲相同則去重)
*/
list.stream().distinct().forEach(System.out::println);
/**
* 3.sorted: 排序
* sorted():默認是按照流中的元素對應的類,實現的comparable接口中的方法進行排序
* 也可以在()內將流中的數據按照指定的規則進行排序
*/
list.stream().sorted().forEach(System.out::println);
list.stream().sorted((e1,e2)->e1.length()-e2.length()).forEach(System.out::println);
/**
* 4.skip & limit (這兩個方法一般會聯合使用)
* skip(): 跳過指定個數的元素
* limit():截取指定個數的元素
*/
list.stream().skip(2).forEach(System.out::println);
list.stream().limit(2).forEach(System.out::println);
//如何獲取 hello TimePause?
list.stream().skip(2).limit(2).forEach(System.out::println);
/**
* 5. map & flatMap (重要!!!)
* map: 對流中的數據進行映射,用新的數據替換舊的數據
* flatMap: 也是元素的映射,不過是扁平化的映射, 將容器中所有元素取出放到集合中
*/
list.stream().map(e->e+".txt").forEach(System.out::println);
String[] strs = {"hello world", "hello csdn", "hello TimePause", "hello csdn"};
//數組也可以使用Stream
Arrays.stream(strs).map(String::toCharArray).forEach(e-> System.out.println(Arrays.toString(e)));
Arrays.stream(strs).map(e->e.split(" ")).flatMap(Arrays::stream).forEach(System.out::println); //strs->map(arrays)->flatMap(elements)
}
}
/**
* 測試Stream中間操作之maptoint (對對象組成的集合進行操作)
*
* @author TimePause
* @create 2020-05-05 20:58
*/
public class TestStream4 {
public static void main(String[] args) {
ArrayList<Persons> person = new ArrayList<>();
Collections.addAll(person,new Persons("小a",14),new Persons("小b",12),new Persons("小c",33));
//Integer sum = person.stream().map(e -> e.getAge()).reduce((e1, e2) -> e1 + e2).get();
//System.out.println(sum);
/**
* maptoInt: 將流中的數據轉成int,此時這個方法的返回值不再是Stream
* 此時這個方法的返回值不再是Stream而是IntStream
*/
double average = person.stream().mapToInt(Persons::getAge).average().getAsDouble();//獲取平均年齡
System.out.println(average);
}
}
class Persons{
String name;
int age;
public Persons(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
}
綜合案例
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* 綜合練習
*
* @author TimePause
* @create 2020-05-05 21:27
*/
public class TestStream5 {
public static void main(String[] args) {
ArrayList<Student> studentList = new ArrayList<>();
Collections.addAll(studentList,
new Student("小1",59),
new Student("小2",99),
new Student("小3",87),
new Student("小4",68),
new Student("小5",71)
);
//1.求出所有及格的學生
System.out.println("---------------1.求出所有及格的學生--------------");
studentList.
stream().
filter(e->e.getScore()>60).
forEach(System.out::println);
//2.求出所有及格的學生的姓名
System.out.println("---------------2.求出所有及格的學生的姓名--------------");
studentList.
stream().
filter(e->e.getScore()>60).
map(Student::getName).
forEach(System.out::println);
//3.求出所有學生的平均成績
System.out.println("---------------3.求出所有學生的平均成績--------------");
double averageScore = studentList.
stream().
mapToInt(Student::getScore).
average().getAsDouble();
System.out.println(averageScore);
//4.求班級前三名(做成集合)???
System.out.println("---------------4.求班級前三名(做成集合)--------------");
List<Student> collect = studentList.
stream().
sorted((e1, e2) -> e2.getScore() - e1.getScore()).
limit(3).
collect(Collectors.toList());
collect.forEach(System.out::println);
//5.求班級的3-10名(做成集合)
System.out.println("---------------5.求班級的3-10名(做成集合)--------------");
List<Student> collect2 = studentList.
stream().
sorted((e1, e2) -> e2.getScore() - e1.getScore()).
limit(10).
skip(2).
collect(Collectors.toList());
collect2.forEach(System.out::println);
//6.求所有不及格學生的平均成績
System.out.println("---------------6.求所有不及格學生的平均成績--------------");
double averageScore2 = studentList.
stream().
filter(e -> e.getScore() < 60).
mapToInt(e -> e.getScore()).
average().
getAsDouble();
System.out.println(averageScore2);
//7.將及格的學生, 按照成績降序輸出所有信息
System.out.println("---------------7.將及格的學生, 按照成績降序輸出所有信息--------------");
studentList.
stream().
filter(e->e.getScore()>60).
sorted((e1,e2)->e2.getScore()-e1.getScore()).
forEach(System.out::println);
//8.班級學生的總分=>從這裏可以看出很多時候e->e.getScore()可以替換Student::getScore,都是對get()方法的引用
System.out.println("---------------8.班級學生的總分--------------");
int res = studentList.stream().mapToInt(Student::getScore).reduce((e1, e2) -> e1 + e2).getAsInt();
int res2 = studentList.stream().mapToInt(e->e.getScore()).reduce((e1, e2) -> e1 + e2).getAsInt();
int res3 = studentList.stream().mapToInt(e -> e.getScore()).sum();
System.out.println(res);
System.out.println(res2);
System.out.println(res3);
}
}
class Student{
private String name;
private int score;
public Student(String name,int score ){
this.name=name;
this.score = score;
}
public String getName(){
return name;
}
public int getScore(){
return score;
}
@Override
public String toString() {
return String.format("姓名: %s, 成績: %d", name, score);
}
}
聰明的人能夠看到本文配套學習視頻哦: https://www.bilibili.com/video/BV1N7411v796?p=1