java8新特性學習
java8的特點
- 速度更快(修改了HasMap、HasSet变勇、CurrentHasMap等存儲結(jié)構(gòu))
- 代碼更少(增加了新的語法Lambda表達式)
- 強大的Stream API
- 便于并行
- 最大化減少空指針異常
1.Lambda表達式
1.1对雪、Lambda表達式是什么缆巧?
Lambda是一個匿名函數(shù)司抱,我們可以把Lambda表達式理解為一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣傳遞)绪励∪乜玻可以寫出更簡潔澳眷、更靈活的代碼。作為一種更緊湊的代碼風格蛉艾,使Java的語言表達能力得到了提升钳踊。
1.2、Lambda表達式的基礎(chǔ)語法
Java8中引入了一個新的操作符“->”勿侯,該操作符稱為箭頭操作符或者Lambda操作符拓瞪,該操作符將Lambda表達式拆分成兩個部分:
左側(cè):Lambda表達式的參數(shù)列表。
右側(cè):Lambda表達式中需要執(zhí)行的功能助琐,即Lambda體祭埂。
語法格式一:無參數(shù),無返回值
() -> System.out.println("hello Lambda");
語法格式二:有一個參數(shù)弓柱,并且無返回值
(x) -> System.out.println(x);
語法格式三:若只有一個參數(shù)沟堡,小括號可以省略不寫
x -> System.out.println(x);
語法格式四:有兩個以上的參數(shù),有返回值矢空,并且Lambda體中有多條語句
Comparator<Integer> com = (x,y) -> {
System.out.println("函數(shù)式接口");
return Integer.compare(x,y);
};
語法格式五:若Lambda體中只有一條語句航罗,return和大括號都可以省略不寫
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
語法格式六:Lambda表達式的參數(shù)列表的數(shù)據(jù)類型可以省略不寫,因為JVM編譯器通過上下文推斷出數(shù)據(jù)類型
(Integer x,Integer y) -> Integer.compare(x,y);
源代碼
package com.zgy.deom1;
import java.util.Comparator;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
public class TestLambda2 {
/*
* Lambda基本語法:參數(shù)列表 -> 方法體
*/
/*
* 語法格式一:無參數(shù)屁药,無返回值
* () -> System.out.println("Hello World");
*/
@Test
public void test1() {
int num = 0; //jdk8中當匿名內(nèi)部類中使用的變量粥血,會自動隱式添加final關(guān)鍵字,匿名內(nèi)部類中依然不能將變量進行加減操作酿箭。
Runnable r = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("hello world"+num);
}
};
r.run();
System.out.println("=============================");
Runnable r1 = () -> System.out.println("hello lambda"+num);
r1.run();
}
/*
* 語法格式二:有一個參數(shù)复亏,沒有返回值
*/
@Test
public void test2() {
Consumer<String> c = (x) -> System.out.println(x);
c.accept(1+"");
//Lambda表達式中如果參數(shù)只有一個,那么括號可以省略不寫
Consumer<Object> c1 = x -> System.out.println("哈哈哈");
c1.accept("ABC");
}
/*
* 語法格式三:有兩個參數(shù)缭嫡,有返回值缔御,Lambda體中有多條語句
*/
@Test
public void test3() {
Comparator<Integer> com = (x,y) -> {
System.out.println("好好學習,天天向上妇蛀!");
return x+y;
};
int result = com.compare(20, 30);
System.out.println("result:"+result);
//如果Lambda體中只有一條語句耕突,可以省略“{}”和return
Comparator<Integer> c = (Integer x,Integer y) -> Integer.compare(x, y);
int num = c.compare(30, 30);
System.out.println("maxNub"+num);
//由于JVM的類型推斷笤成,所以Lambda參數(shù)列表的數(shù)據(jù)類型,可以省略不寫
Comparator<Integer> c1 = (x,y) -> Integer.compare(x, y);
int num1 = c1.compare(30, 31);
System.out.println("maxNub"+num1);
}
/*
* 調(diào)用自定的函數(shù)式接口
*/
@Test
public void test4() {
MyFun myFun = x -> x+100;
int value = myFun.getValue(100);
System.out.println("value:"+value);
}
}
package com.zgy.deom1;
@FunctionalInterface
public interface MyFun {
int getValue(int x);
}
練習代碼
package com.zgy.deom1;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;
public class TestLambda {
List<Employee> emps = Arrays.asList(
new Employee(101,"張三",18,9999.99),
new Employee(102,"李四",59,6666.66),
new Employee(103,"王五",28,3333.33),
new Employee(104,"趙六",8,7777.77),
new Employee(105,"田七",38,5555.55)
);
/*
* 使用Collections對員工列表進行排序眷茁,按工資從多到少比
* 關(guān)于Collections.sort()自定義排序規(guī)則炕泳,兩個參數(shù)x和y,如果要按照升序上祈,就是x在前面培遵;相反如果要按照降序,就是y在前面登刺。
*/
@Test
public void test1() {
Collections.sort(emps, (x,y) -> Double.compare(y.getSalary(), x.getSalary()));
for (Employee employee : emps) {
System.out.println(employee);
}
}
/*
* 定義一個函數(shù)式接口籽腕,傳如一個參數(shù),返回該參數(shù)的大寫形式
*/
@Test
public void test2() {
String str = strHandler("zgy", x -> x.toUpperCase());
System.out.println("str:"+str);
}
//小寫轉(zhuǎn)大寫的方法
public String strHandler(String str,MyFunction mf) {
return mf.getValue(str);
}
/*
* 聲明一個帶兩個泛型的函數(shù)式接口纸俭,泛型類型為<T,R> T為參數(shù)节仿,R為返回值,接口中聲明對應的抽象方法
* 再使用接口作為參數(shù)掉蔬,計算兩個long型參數(shù)的和
* 再計算兩個long型參數(shù)的積
*/
@Test
public void test3() {
//計算和
System.out.println(getValue(100L, 200L, (x,y) -> x+y));
//計算積
System.out.println(getValue(500L, 900L, (x,y) -> x*y));
}
public long getValue(Long x,Long y,MyFunction2<Long, Long> mf) {
return mf.getValue(x, y);
}
}
package com.zgy.deom1;
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee() {}
public Employee(int id, String name, int age, double salary) {
super();
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
package com.zgy.deom1;
@FunctionalInterface
public interface MyFunction {
public String getValue(String x);
}
package com.zgy.deom1;
@FunctionalInterface
public interface MyFunction2<T,R> {
public R getValue(T t1, T t2);
}
1.3、函數(shù)式接口
函數(shù)式接口:接口中只有一個抽象方法的接口矾瘾,稱為函數(shù)式接口女轿,可以使用注解@FunctionalInterface修飾,該注解的作用是可以檢查該接口是否為函數(shù)式接口壕翩。
1.4蛉迹、Java8內(nèi)置四大核心函數(shù)式接口
Consumer<T>:消費型接口
? void accept(T t);
Supplier<T>:供給型接口
? T get();
Function<T, R>:函數(shù)型接口
? R apply(T t);
Predicate<T>:斷言型接口
? boolean test(T t);
其它子接口如下圖
源代碼
package com.zgy.deom1;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
/*
* JDK8 內(nèi)置四大核心函數(shù)式接口
*/
public class TestLambda3 {
/*
* 消費式接口:傳一個參數(shù),沒有返回值
* Consumer<T> void accept(T t);
*/
@Test
public void test1() {
say("好好學習放妈,天天向上北救!", x -> System.out.println(x+new Date()));
}
public void say(String str, Consumer<String> c) {
c.accept(str);
}
/*
* 供給式接口:沒有參數(shù),返回一個結(jié)果
* Supplier<T> T get();
*/
@Test
public void test2() {
List<Integer> listResult = getNumberArr(() -> {
List<Integer> list = new ArrayList<>();
for(int i=0; i<10; i++) {
list.add(new Random().nextInt());
}
return list;
});
for (Integer integer : listResult) {
System.out.println("DATA:"+integer);
}
}
public List<Integer> getNumberArr(Supplier<List<Integer>> s){
return s.get();
}
/*
* 函數(shù)式接口:傳入一個參數(shù)芜抒,返回一個結(jié)果
* Function<T,R> R apply(T t);
*/
@Test
public void test3() {
System.out.println(apply("ZGY:", (x) -> x+new Date()));
}
public String apply(String a, Function<String, String> f) {
return f.apply(a);
};
/*
* 斷言型接口:傳入一個參數(shù)珍策,返回一個布爾值
* Predicate<T> boolean test(T t);
*/
@Test
public void test4() {
System.out.println(test(40, x -> x>0));
}
public String test(int x, Predicate<Integer> p) {
if(p.test(x)) {
return "大于0";
}else {
return "小于0";
}
}
}
1.5、方法引用與構(gòu)造器引用
-
方法引用:若Lambda體中的內(nèi)容有方法已經(jīng)實現(xiàn)了宅倒,我們可以使用“方法引用”(可以理解為方法引用是Lambda表達式的另外一種表現(xiàn)形式)
主要有三種語法格式:
- 對象 :: 實例方法名
- 類 :: 靜態(tài)方法名
- 類 :: 實例方法名
注意:
- Lambda體中調(diào)用方法的參數(shù)列表與返回值類型攘宙,要與函數(shù)式接口中抽象方法的函數(shù)列表和返回值類型保持一致。
- 若Lambda參數(shù)列表中的第一個參數(shù)是實例方法的調(diào)用者拐迁,第二個參數(shù)是實例方法的參數(shù)蹭劈,可以用使用ClassName :: method
-
構(gòu)造器引用:
格式:ClassName :: new
注意:需要調(diào)用的構(gòu)造器參數(shù)列表要與函數(shù)式接口中抽象方法的參數(shù)列表保持一致
-
數(shù)組引用:
格式:Type :: new;
源代碼
package com.zgy.deom1;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
public class TestMethodRef {
/*
* 方法引用和構(gòu)造器引用
*/
@Test
public void test1() {
//對象 :: 實例方法名
Consumer<String> c = System.out::println;
c.accept("ZGY");
Employee e = new Employee(111,"ZGY",22,1000);
Supplier<String> s = e::getName;
System.out.println(s.get());
}
@Test
public void test2() {
//類名::方法名
Comparator<Integer> c = Integer::compare;
System.out.println(c.compare(800, 210));
}
@Test
public void test3() {
//類名::實例方法名
BiPredicate<String, String> bp = String::equals;
System.out.println(bp.test("AAA", "AAA"));
}
@Test
public void test4() {
//構(gòu)造器引用
Supplier<Employee> e = Employee::new;
Employee employee = e.get();
employee.setName("ZGY");
System.out.println(employee);
}
}
2、Stream API
2.1线召、什么是Stream
流(Stream)到底是什么铺韧?
流,是數(shù)據(jù)渠道缓淹,用于操作數(shù)據(jù)源(集合哈打、數(shù)組等)所生成的元素序列塔逃。集合講的是數(shù)據(jù),而流講的是計算前酿!
注意:
- Stream自己不會存儲元素患雏。
- Stream不會改變源對象,相反他們會返回一個持有結(jié)果的新Stream罢维。
- Stream是延遲執(zhí)行的淹仑,這意味著它們會等到需要結(jié)果的時候才會執(zhí)行。
2.2肺孵、Stream的操作三個步驟
-
創(chuàng)建Stream源碼
package com.zgy.deom1; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.Test; /* * 創(chuàng)建Stream的幾種方式 */ public class TestStreamAPI1 { @Test public void test1() { //1.可以通過Collection系列集合提供的stream()或parallelStream() List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); //2.通過Arrays中的靜態(tài)方法stream()獲取數(shù)組流 Employee[] employees = new Employee[10]; Stream<Employee> stream2 = Arrays.stream(employees); //3.通過Stream類中的靜態(tài)方法of() Stream<String> stream3 = Stream.of("AA","BB","CC","DD"); //4.通過迭代的方式創(chuàng)建無限流 Stream<Integer> stream4 = Stream.iterate(0, x -> x+2); stream4.limit(5).forEach(System.out::println); //5.通過生成的方式創(chuàng)建無限流 Stream<Double> stream5 = Stream.generate(() -> Math.random()); //下面limit匀借、forEach分別是 stream5.limit(20).forEach(System.out::println); } }