這裏介紹一下lambda表達式,主要是平時寫代碼常用的,也許不全面,但是常用的我會很詳細的介紹的,其原理大家有興趣可以自己探索,我看中的是它的應用價值,廢話不多說,直接進入主題。
lambda表達式形式其實比較簡單的,如下,基本上都這樣的,更加具體的,看如下代碼
() -> statement
() -> {statement}
(param1,param2...) -> statement
(param1,param2...) -> {statement}
定義一個基本的實體類:
package com.chenjianwen.test;
/**
* @Description: <br>
* @Date: Created in 2019/9/23 <br>
* @Author: chenjianwen
*/
public class Userz {
private String phone;
private String name;
private Integer age;
public Userz(){}
public Userz(String phone, String name, Integer age){
this.phone = phone;
this.name = name;
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Userz{" +
"phone='" + phone + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
一、匿名內部類
1)創建線程的時候,爲了快捷方便,經常要使用到匿名內部類,如下這樣:
@Test
public void test01(){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello world");
}
}).start();
}
這樣已經很簡潔了,但是使用lambda表達式還可以更加的簡潔的,如下這樣:
@Test
public void test02(){
new Thread(() -> System.out.println("hello java")).start();
}
2)在使用比較器Comparator時也可以使用lambda表達式
@Test
public void test03(){
List<Userz> userzList = new ArrayList<>();
userzList.add(new Userz("13023344555", "chen1", 12));
userzList.add(new Userz("13023344555", "chen2", 14));
userzList.add(new Userz("13023344555", "chen3", 5));
userzList.add(new Userz("13023344555", "chen4", 67));
System.out.println("排序前");
for(Userz u : userzList){
System.out.println(u.toString());
}
System.out.println("排序後");
Collections.sort(userzList, new Comparator<Userz>() {
@Override
public int compare(Userz o1, Userz o2) {
return o1.getAge() - o2.getAge();
}
});
for(Userz u : userzList){
System.out.println(u.toString());
}
}
其中,這裏使用了比較器的:
這裏的比較器換成lambda表達式的話,就簡便多了,如下:
Collections.sort(userzList, (x,y) -> x.getAge()-y.getAge());
或者這樣也可以:
userzList.sort((x,y) -> x.getAge()-y.getAge());
或者這樣:
Collections.sort(userzList, Comparator.comparing(Userz::getAge));
二、集合操作
1)集合的遍歷
平時我們集合的遍歷都使用過foreach方法,如下:
@Test
public void test04(){
List<String> list = Arrays.asList("a","b","c","hello","girl");
for(String s : list){
System.out.println(s);
}
}
現在有了lambda,就可以使用這種方式:
@Test
public void test04(){
List<String> list = Arrays.asList("a","b","c","hello","girl");
list.forEach(y -> System.out.println(y));
}
或者這樣:
@Test
public void test04(){
List<String> list = Arrays.asList("a","b","c","hello","girl");
list.forEach(System.out::println);
}
2)filter()的使用
假如上面的list集合我想篩選出長度大於3的字符串,那該怎麼辦:
如果不使用lambda表達式,需要foreach遍歷在判斷,如下:
@Test
public void test06(){
List<String> list = Arrays.asList("a","b","c","hello","girl");
List<String> res = new ArrayList<>();
for(String s : list){
if(s.length() > 3){
res.add(s);
}
}
res.forEach(System.out::println);
}
如果使用lambda表達式,就這樣:
@Test
public void test07(){
List<String> list = Arrays.asList("a","b","c","hello","girl");
list.stream().filter(x -> x.length() > 3).collect(Collectors.toList()).forEach(System.out::println);
}
是不是感覺簡潔許多。
3)map()的使用
有時候已知一個對象的集合,我們需要將這個對象中的某個變量重新放在一個集合中,例如上面的list<Userz>集合中我要獲取其變量name的集合list<String>,需要foreach遍歷集合,如下:
@Test
public void test08(){
List<Userz> userzList = new ArrayList<>();
userzList.add(new Userz("13023344555", "chen1", 12));
userzList.add(new Userz("13023344555", "chen2", 14));
userzList.add(new Userz("13023344555", "chen3", 5));
userzList.add(new Userz("13023344555", "chen4", 67));
List<String> res = new ArrayList<>();
for(Userz u : userzList){
res.add(u.getName());
}
for(String s : res){
System.out.println(s);
}
}
如果使用lambda表達式就簡潔很多:
@Test
public void test09(){
List<Userz> userzList = new ArrayList<>();
userzList.add(new Userz("13023344555", "chen1", 12));
userzList.add(new Userz("13023344555", "chen2", 14));
userzList.add(new Userz("13023344555", "chen3", 5));
userzList.add(new Userz("13023344555", "chen4", 67));
userzList.stream().map(t -> t.getName()).collect(Collectors.toList()).forEach(x -> System.out.println(x));
}
只需上述一行代碼即可,
也可以這麼寫:
userzList.stream().map(Userz::getName).collect(Collectors.toList()).forEach(System.out::println);
4)Predicate的使用
有時候filter()需要多重篩選條件,例如如下字符串集合,我需要篩選出以c開頭,且長度大於3的字符串;一共兩重篩選條件,而且有可能n多重篩選條件,就需要用到Predicate:
@Test
public void test10(){
List<String> list = Arrays.asList("c", "go", "java", "hello world", "python", "come on", "cnmlgb");
Predicate<String> p1 = (x) -> x.startsWith("c");
Predicate<String> p2 = (y) -> y.length() > 3;
list.stream().filter(p1.and(p2)).forEach(System.out::println);
list.stream().filter(p1.or(p2)).forEach(System.out::println);
}
其中,and()是將兩個同時滿足條件的查出來,or()將兩者之一查出來。
5)reduce()的使用
reduce()共有三個重載方法,如下:
第一個方法,假如說Stream中的元素爲a[0],a[1],..,a[n],用java代碼表示爲
T result = a[0];
for(int i = 1;i < n;i++){
result = accumulator.apply(result, a[i]);
}
return result;
他在四則運算,求最值,或字符串拼接等又很多運用,如下演示:
/**
* 求和
*/
@Test
public void test12() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}).get();
System.out.println(value);
}
@Test
public void test13() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce((x, y) -> x + y).get();
System.out.println(value);
}
/**
* 求最大值
*/
@Test
public void test14() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer > integer2 ? integer : integer2;
}
}).get();
System.out.println(value);
}
@Test
public void test15() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce((x, y) -> x > y ? x : y).get();
System.out.println(value);
}
/**
* 字符串拼接
*/
@Test
public void test18(){
Stream<String> s = Stream.of("I", " " ,"am", " ", "the" , " ", "God");
String value = s.reduce(new BinaryOperator<String>() {
@Override
public String apply(String s, String s2) {
return s.concat(s2);
}
}).get();
System.out.println(value);
}
第二個相當於第一個方法有個初始值,算是加強版,用java代碼表示如下
T result = identity;
for(int i = 0;i < n;i++){
result = accumulator.apply(result, a[i]);
}
return result;
針對於上述的四則運算,最值和字符串拼接如下
/**
* 求和
*/
@Test
public void test12() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(5, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
});
System.out.println(value);
}
@Test
public void test13() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(5, (x, y) -> x + y);
System.out.println(value);
}
/**
* 求最大值
*/
@Test
public void test14() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(5 ,new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer > integer2 ? integer : integer2;
}
});
System.out.println(value);
}
@Test
public void test15() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(5, (x, y) -> x > y ? x : y);
System.out.println(value);
}
/**
* 字符串拼接
*/
@Test
public void test18(){
Stream<String> s = Stream.of(" " ,"am", " ", "the" , " ", "God");
String value = s.reduce("I", new BinaryOperator<String>() {
@Override
public String apply(String s, String s2) {
return s.concat(s2);
}
});
System.out.println(value);
}
第三個很複雜的
其方法裏面又三個參數:
第一個參數:identity,泛型爲U,與reduce方法的返回值一致,但是此時Stream中元素的泛型爲T,換言之,兩者類型不一致,這樣的話,操作空間就非常大了,不管Stream中存儲的是什麼類型,U都可以是任意類型,包括基本類型的包裝類,如Integer,Long等,或者是對象類String等,或者是集合ArrayList等。
第二個參數:accumulator,其類型是BiFunction,輸入的是U和T兩種類型的數據,而返回的是U類型。
第三個參數:combiner,類型是BinaryOperator,支持對U類型的對象進行操作。如果Stream是非並行的,此參數不生效;只有並行時才生效。
1、非並行時,combiner不生效,其java代碼表述如下:
U result = identity;
for (T element:a) {
result = accumulator.apply(result, element);
}
return result;
仔細觀察,result類型是U,而Element類型是T。如果U與T一致,那麼就與上述一個或兩個參數的reduce方法一樣了。正因爲可以不一樣,就存在多種用法,假設U的類型是ArrayList,那麼可以將Stream中所有元素添加到ArrayList中,如下:
@Test
public void test19(){
Stream<String> s1 = Stream.of("b", "ab", "bbc" ,"ppg");
s1.reduce(new ArrayList<String>(), new BiFunction<ArrayList<String>, String, ArrayList<String>>() {
@Override
public ArrayList<String> apply(ArrayList<String> strings, String s) {
strings.add(s);
return strings;
}
}, new BinaryOperator<ArrayList<String>>() {
@Override
public ArrayList<String> apply(ArrayList<String> strings, ArrayList<String> strings2) {
return strings;
}
}).forEach(System.out::println);
}
其lambda表達式爲:
@Test
public void test21(){
Stream<String> s1 = Stream.of("b", "ab", "bbc" ,"ppg");
s1.reduce(new ArrayList<String>(), (x,y) -> {x.add(y);return x;}, (a,b) -> a).forEach(System.out::println);
}
也可以使用Predicate對元素進行過濾:
@Test
public void test20(){
Stream<String> s1 = Stream.of("b", "ab", "bbc" ,"ppg");
Predicate<String> predicate = t -> t.contains("b");
s1.reduce(new ArrayList<String>(), new BiFunction<ArrayList<String>, String, ArrayList<String>>() {
@Override
public ArrayList<String> apply(ArrayList<String> strings, String s) {
if(predicate.test(s)){
strings.add(s);
}
return strings;
}
}, new BinaryOperator<ArrayList<String>>() {
@Override
public ArrayList<String> apply(ArrayList<String> strings, ArrayList<String> strings2) {
return strings;
}
}).forEach(System.out::println);
}
其lambda表達式爲:
@Test
public void test22(){
Stream<String> s1 = Stream.of("b", "ab", "bbc" ,"ppg");
Predicate<String> predicate = t -> t.contains("b");
s1.reduce(new ArrayList<String>(), (x,y) -> {if(predicate.test(y)) x.add(y);return x;}, (a,b) -> a).forEach(System.out::println);
}
2、並行時,第三個參數就有意義了,它將不同線程計算的結果調用combiner做彙總後返回,由於採用了並行計算,與非並行時計算的結果也有了差異,例如,計算1+2+3+4的值,其中4爲初始值:
@Test
public void test23(){
System.out.println(Stream.of(1,2,3).parallel().reduce(4, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}));
}
/**
* lambda表達
*/
@Test
public void test24(){
System.out.println(Stream.of(1,2,3).parallel().reduce(4, (x,y) -> x+y, (a,b) -> a+b));
}
計算結果應該是10的纔對,然而實際結果時18,實際計算結果是這樣的:
對於第一個BiFunction重寫的apply()方法,線程一:4+1=5,線程2:4+2=6,線程3:4+3=7
然後對於第二個BinaryOperator重寫方法apply()方法將上述結果相加:5+6+7=18。
換個測驗,將BinaryOperator重寫方法apply()方法換成相乘
@Test
public void test23(){
System.out.println(Stream.of(1,2,3).parallel().reduce(4, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer * integer2;
}
}));
}
/**
* lambda表達
*/
@Test
public void test24(){
System.out.println(Stream.of(1,2,3).parallel().reduce(4, (x,y) -> x+y, (a,b) -> a*b));
}
其運算過程是:(4+1)*(4+2)*(4+3) = 210;
其效果與下列寫法一致:
@Test
public void test25(){
System.out.println(Stream.of(1,2,3).map(x -> x+4).reduce((y,z) -> y*z).get());
}
暫時這麼多,以後想到再繼續補充。。。