一、基礎
基本語法
[接口聲明] = (參數)-> {代碼。。。};
具體使用
1.表達式必須和接口綁定使用,符合接口的方法聲明。
如某個接口:
public interface A {
String get(int i);
}
聲明接口時:
A a = new A() {
@Override
public String get(int i) {
return "hello " + i;
}
};
使用lambda
A la = (int i) -> {
return "lambda " + i;
};
但如果一個接口有多個方法,則無法使用lambda表達式。如
public interface B {
String get(int i);
void set();
}
default和static方法除外
public interface A {
String get(int i);
default int fun1() {
return 0;
}
static String fun2() {
return "A";
}
}
2.參數:可以是0到n個參數,具體根據綁定的接口而定,且參數類型可以不寫。
A la = (i) -> {
return "lambda " + i;
};
3.返回值:如果代碼塊只有一行,可以沒有大括號。
沒有大括號時,不能用return關鍵字。
如果添加了大括號,或者有多行代碼,必須通過關鍵字return返回。
//可以這樣
A la2 = (i) -> "lambda " + i;
//也可以這樣
A la2 = (i) -> {
return "lambda " + i;
};
//而不能這樣
A la2 = (i) -> return "lambda " + i;
java.util.function提供了大量的函數式接口供我們使用
Predicate,傳入T類型,返回boolean
Consumer,傳入T類型,消耗掉,返回void
function,傳入T類型,返回E類型
Supplier,傳入返回T類型
UnaryOperator,傳入T,返回T
BinaryOperator,傳入兩個T類型,返回T類型
emmm,記不住~~~
如Predicate
Predicate<String> pre1 = (name) -> "admin".equals(name);
System.out.println(pre1.test("admin") ? "管理員" : "普通用戶");
打印結果:
管理員
lambda對this關鍵字的優化
使用lambda表達式時,在表達式內部this代表的是該lambda表達式所屬的對象。
正常情況下,在接口內部使用this,該this指的是這個接口,而不是聲明該接口的對象(或者說該接口所屬的對象)
public class Test{
String s1 = "全局變量";
public void testLambda() {
String s2 = "局部變量";
new Thread(new Runnable() {
@Override
public void run() {
String s3 = "內部變量";
System.out.println(this.s1);//報錯!!!
System.out.println(s2);
System.out.println(s3);
}
}).start();
}
public static void main(String[] args) {
new App().testLambda();
}
}
public class Test {
String s1 = "全局變量";
public void testLambda() {
String s2 = "局部變量";
new Thread(() -> {
String s3 = "內部變量";
System.out.println(this.s1);//不報錯!!!
System.out.println(s2);
System.out.println(s3);
}).start();
}
public static void main(String[] args) {
new App().testLambda();
}
}
打印結果:
全局變量
局部變量
內部變量
方法重載對lambda表達式的影響
當遇到重載方法時,如果方法中傳入的接口中的方法參數類型和返回值一樣,則無法使用lambda。如
public class Test {
public static void main(String[] args) {
test(() ->{//報錯!!!
System.out.println("I1");
return 1;
});
}
//重載方法
private static void test(A a) {
a.printString();
}
private static void test(B b) {
b.printInt();
}
}
interface A {
void printString();
}
interface B {
void printInt();
}
二、lambda在集合中的應用
方法引用
1. 靜態方法、實例方法引用
以List.sort() 為例
實體類:
class Person {
private String name;
private int age;
public static int compareByAge(Person p1, Person p2) {
return p1.age - p2.age;
}
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
setter and getter...
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
數據源
List<Person> persons = new ArrayList<>();
persons.add(new Person("Tom", 28));
persons.add(new Person("Jack", 30));
persons.add(new Person("Dav", 20));
使用內部類方式
//使用內部類方式
persons.sort(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
int i = o1.getAge() - o2.getAge();
return i;
}
});
使用lambda方式
//使用lambda方式
persons.sort((a, b) -> a.getAge() - b.getAge());
persons.sort(Comparator.comparingInt(Person::getAge));
先在任意一個類中定義參數為(Person p1, Person p2),返回類型為 int 的靜態方法和普通方法
class PersonUtil {
public int compareByAge(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
public static int compare(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}
靜態方法引用,類名::靜態方法名
//靜態方法的引用,會默認將(Person, Person)參數傳遞給PersonUtil的compare方法
persons.sort(PersonUtil::compare);
實例方法引用,對象::普通方法名
//實例方法的引用
PersonUtil pu = new PersonUtil();
persons.sort(pu::compareByAge);
2. 構造方法引用
除了靜態方法引用和實例方法引用還有構造方法引用。
interface IPerson {
//通過指定類型的構造方法初始化對象數據,
//即:Person類中必須有(String name, int age)類型參數的構造方法。
Person initPerson(String name, int age);
}
//構造方法的引用
IPerson ip = Person::new;
Person wang = ip.initPerson("wang", 25);
Stream
Stream解耦了對數據的各種操作,比起傳統的手動操作數據更方便。
例如:
List<String> list = new ArrayList<>();
list.add("abc");
list.add("adcd");
list.add("adcde");
list.add("adcdef");
//傳統方式獲取list中元素長度>=4的數據
List<String> result = new ArrayList<>();
for (String s : list) {
if (s.length() >= 4) {
result.add(s);
}
}
System.out.println(result);
//使用Stream方式獲取list中元素長度>=4的數據
List<String> collect = list.stream().filter(s -> s.length() >= 4).collect(Collectors.toList());
System.out.println(collect);
打印結果:
[adcd, adcde, adcdef]
[adcd, adcde, adcdef]
Stream概述
/**
* 1. 獲取Stream對象
* 1. 從集合或數組中獲取[**]
* Collection.stream(),如list.stream()
* Collection.parallelStream()
* Arrays.stream()
* 2. BufferReader
* BufferReader.lines()
* 3. 靜態方法
* java.util.stream.IntStream.range()..
* java.nio.file.Files.walk()..
* 4. 自定義構建
* java.util.Spliterator
* 5. 更多方式。。
* Random.ints()
* Pattern.splitAsStream()..
* 2. 中間操作api
* 操作結果是一個Stream,中間操作可以有一個或多個連續的中間操作,需要注意的是,中間操作只記錄操作方式,
* 不做具體執行,直到借宿操作發生時,才做數據的最終執行。
* 中間操作:就是業務邏輯處理。
* 中間操作過程:
* 無狀態:數據處理是,不搜前置中間操作的影響。
* map/filter/peek/parallel/sequential/unordered
* 有狀態:數據處理時,受前置中間操作的影響。
* distinct/sorted/limit/skip
* 3. 終結操作 | 結束操作{Terminal}
* 需要注意:一個Stream對象,只能由一個Terminal操作,這個操作一旦發生,就會真實的處理數據。
* 終結操作:
* 非短路操作:當前的Stream對象必須處理完集合中所有數據才能得到結果。
* forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator
* 短路操作:當前的Stream對象在處理過程中,一旦滿足某個條件,就可以得到結果。
* anyMatch/allMatch/noneMatch/findFirst/findAnd等
*/
對比上面例子
list.stream() //獲取Stream
.filter(s -> s.length() >= 4) //中間操作,返回一個Stream對象
.collect(Collectors.toList()); //終結操作
Stream的獲取
//多個數據
Stream<String> stream1 = Stream.of("A", "B", "C");
//數組
String[] strArray = new String[]{"A", "B", "C"};
Stream<String> stream2 = Stream.of(strArray);
//列表
ArrayList<Object> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Stream<Object> stream3= list.stream();
//集合
Set<String> set = new HashSet<>();
set.add("A");
set.add("B");
set.add("C");
Stream<String> stream4 = set.stream();
//map
HashMap<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
Stream<Map.Entry<String, Integer>> stream5 = map.entrySet().stream();
Stream對象對于基本數據類型的封裝
// int / long / double
IntStream.of(new int[] {10, 20, 30}).forEach(System.out::println);
IntStream.range(1,5).forEach(System.out::println);
IntStream.rangeClosed(1,5).forEach(System.out::println);
從Stream中獲取指定的類型數據
Stream<String> stream1 = Stream.of("A", "B", "C");
//array
String[] arrayx = stream1.toArray(String[]::new);
//string
String stringx = stream1.collect(Collectors.joining());
//list
List<String> listx = stream1.collect(Collectors.toList());
//set
Set<String> setx = stream1.collect(Collectors.toSet());
//map
Map<String, String> mapx = stream1.collect(Collectors.toMap(x -> "key:" + x, y -> "val:" + y));