1.函數式接口
函數式接口:有且僅有一個抽象方法的接口
1.2 函數式接口作爲方法的參數
/*
定義一個類(RunnableDemo),在類中提供兩個方法
一個方法是:startThread(Runnable r) 方法參數Runnable是一個函數式接口
一個方法是主方法,在主方法中調用startThread方法
*/
public class RunnableDemo {
public static void main(String[] args) {
//匿名內部類
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"啓動了");
}
});
//Lambda表達式
startThread(()->System.out.println(Thread.currentThread().getName()+"啓動了"));
}
private static void startThread(Runnable r){
//Thread t = new Thread(r,"stratThread");
//t.start();
new Thread(r,"stratThread").start();
}
}
運行結果:stratThread啓動了
如果方法的參數是函數式接口,我們可以使用Lambda表達式作爲參數傳遞
- startThread(()->System.out.println(Thread.currentThread().getName()+“啓動了”));
1.3 函數式接口作爲方法的返回
/*
定義一個類(ComparatorDemo),在類中提供兩個方法
一個方法是:Comparator<String> getComparator() 方法返回值Comparator是一個函數式接口
一個方法是主方法:在主方法中調用getComparator()方法
需求:比較字符串的長度,按照長短來排序
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class ComparatorDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaaa");
list.add("bbb");
list.add("sss");
list.add("c");
list.add("cddf");
System.out.println(list); //排序前
Collections.sort(list,getComparator());//第一個參數是集合對象,第二個參數是比較器
System.out.println(list); //排序後
}
//當函數式接口作爲方法的返回值時,返回的是接口的實現類對象
private static Comparator<String> getComparator() {
//使用匿名內部類返回一個Comparator對象
/*Comparator c = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
};
return c;*/
/* return new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
};*/
//Lambda表達式
return (s1, s2) -> s1.length() - s2.length();
}
}
1.4 常用函數式接口
1.4.1 Supplier接口
Supplier接口主要是用來生產數據的,包含一個無參的方法
- T get(): 獲得結果
- 該方法不需要參數,它會按照某種實現邏輯(有Lambda表達式實現)返回一個數據。
- Supplier接口也被稱爲生產型接口,如果我們指定了接口的泛型什麼類型,那麼接口中的get方法就會返回什麼類型的數據給我們使用。
import java.util.Arrays;
import java.util.function.Supplier;
public class SupplierDemo {
public static void main(String[] args) {
Integer[] arr = {12,43,22,67,34,99};
//使用匿名內部類
/*Integer max = getMax(new Supplier<Integer>() {
@Override
public Integer get() {
Arrays.sort(arr);
return arr[arr.length - 1];
}
});
*/
//Lambda表達式
Integer max = getMax(()->{
Arrays.sort(arr);
return arr[arr.length - 1];
});
System.out.println(max);
}
//使用Supplier接口作爲參數,獲取數組中的最大值
private static Integer getMax(Supplier<Integer> s){
return s.get();
}
}
運行結果: 99
1.4.2 Consumer接口
Consumer:包含兩個方法
- void accept(T t):對給定的參數執行此操作
- default Consumer andThen(Consumer after):返回一個組合的Consumer,依次執行此操作,然後執行after操作
- Consumer接口也被稱爲消費型接口,它消費的數據的數據類型由泛型指定
使用accept(T t)
public class ConsumerDemo {
public static void main(String[] args) {
String[] strings = {"路飛,19","索隆,22","山治,21","娜美,19"};
//使用匿名內部類
/*useString(strings, new Consumer<String>() {
@Override
public void accept(String s) {
String[] split = s.split(",");
System.out.println("姓名;"+split[0]+",年齡:"+split[1]);
}
});*/
//Lambda表達式
useString(strings,s-> System.out.println("姓名;"+s.split(",")[0]+",年齡:"+s.split(",")[1]));
}
/**
* @param strarr
* @param con
*/
private static void useString(String[] strarr, Consumer<String> con){
//遍歷數組獲取元素,用於消費
for (String s : strarr) {
con.accept(s);//將String[] strarr集合中的元素依次取出,用於消費
}
}
}
運行結果:姓名;路飛,年齡:19
姓名;索隆,年齡:22
姓名;山治,年齡:21
姓名;娜美,年齡:19
使用Consumer andThen(Consumer after)
public class ConsumerDemo {
public static void main(String[] args) {
String[] strings = {"路飛,19","索隆,22","山治,21","娜美,19"};
//使用兩個Consumer接口對象的方法
//匿名內部類
/* useString2(strings, new Consumer<String>() {
@Override
public void accept(String s) {
System.out.print("姓名;"+s.split(",")[0]);
}
}, new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(",年齡:"+s.split(",")[1]);
}
});
*/
//Lambda表達式
useString2(strings,
s ->System.out.print("姓名;"+s.split(",")[0]),
s -> System.out.println(",年齡:"+s.split(",")[1])
);
}
/**
* @param strarr
* @param con1
* @param con2
* 使用 andThen 方法將 Consumer 對象串聯起來能夠連續執行,消費同一個對象
*/
private static void useString2(String[] strarr,Consumer<String> con1,Consumer<String> con2){
for (String s : strarr) {
con1.andThen(con2).accept(s);//使用兩個Consumer對象對同一個s進行操作
}
}
}
1.4.3 Predicate接口
Predicate接口用於對參數進行判斷是否滿足指定條件
Predicate: 常用的四個方法
boolean
test(T t ): 對給定的參數進行判斷(判斷邏輯由Lambda表達式實現),返回一個布爾值- default Predicate negate():返回一個邏輯的否定,對應邏輯非
- default Predicate and(Predicate other): 返回一個組合的判斷,對應短路與
- default Predicate or(Predicate other): 返回一個組合的判斷,對應短路或
① test、negate方法使用
public class Demo {
public static void main(String[] args) {
/* boolean result = usePredicate("hello", new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length()>8;
}
});*/
boolean b1 = usePredicate("hello", s -> s.length()>8);
System.out.println(b1);
boolean b2 = usePredicate("hellowrold", s -> s.length() < 15);
System.out.println(b2);
}
//
//判斷字符串是否滿足條件
private static boolean usePredicate(String str,Predicate<String> p){
// boolean test = p.test(str);
// return p.test(str);
return p.negate().test(str);
}
}
運行結果: true
false
② and(Predicate other)、or(Predicate other)方法使用
public class Demo2 {
public static void main(String[] args) {
boolean b = checkString("hello", s -> s .length() > 8, s -> s.length() < 15);
System.out.println(b);
}
//同一個字符串給出兩個不同的判斷條件,最後把這兩個判斷的結果做邏輯與運算和邏輯或運算
private static boolean checkString(String s, Predicate<String> p1, Predicate<String> p2) {
// boolean b1 = p1.test(s);
// boolean b2 = p2.test(s);
// return b1&&b2;
//使用and、or方法
// return p1.and(p2).test(s);
return p1.or(p2).test(s);
}
}
運行結果:true
練習
- String[] strArr = {“林青霞,30”, “柳巖,43”, “張曼玉,35”, “貂蟬,28”, “王祖賢,33”};
- 字符串數組中有多條信息,請通過Predicate接口的拼裝將符合條件的字符串篩選到集合ArrayList中,並遍歷集合
- 同時滿足如下要求:姓名長度大於2,年齡大於33
public class PredicateDemo1 {
public static void main(String[] args) {
String[] strArr = {"林青霞,30", "柳巖,43", "張曼玉,35", "貂蟬,28", "王祖賢,33"};
//調用usePredicate方法
//匿名內部類
/* usePredicate(strArr, new Predicate<String>() {
@Override
public boolean test(String s) {//判斷姓名長度是否大於3
return s.split(",")[0].length() > 3;
}
}, new Predicate<String>() {
@Override
public boolean test(String s) {//判斷年齡是否大於33
return Integer.parseInt(s.split(",")[1]) > 33;
}
});*/
//Lambda表達式
usePredicate(strArr,
s -> s.split(",")[0].length() > 2, //篩選姓名長度大於2的
s -> Integer.parseInt(s.split(",")[1]) > 33 //篩選年齡大於33的
);
}
private static void usePredicate(String[] strings, Predicate<String> pre1, Predicate<String> pre2) {
//用於將存放滿足條件的數據
ArrayList<String> list = new ArrayList<>();
//遍歷字符串數組
for (String string : strings) {
boolean test = pre1.and(pre2).test(string); //判斷是否滿足條件
if (test) {//如果test爲true,那麼將strings放入list中
list.add(string);
}
}
//遍歷ArrayList
for (String s : list) {
System.out.println(s);
}
}
}
運行結果: 張曼玉,35
1.4.4 Function接口
Function<T , R >接口通常用於對參數進行處理,轉換(處理邏輯由Lambda表達式實現),然後返回一個新的值常用的兩個方法
- R apply(T t):將此函數應用於給定的參數
- default Function andThen(Function after):返回一個組合函數,首先將該函數應用於輸入,然後將after函數應用於結果
public class Demo2 {
public static void main(String[] args) {
//定義一個方法,把一個字符串裝換成int型,輸出
// convert("100",s->Integer.parseInt(s));
//定義一個方法,把一個int型數據加上一個值後轉成字符串,輸出
// convert(12,integer -> String.valueOf(integer+21));
//定義一個方法,把一個字符串轉換成int型,把int型的數據加上一個整數後,轉爲字符串輸出
convert("12", s -> Integer.parseInt(s), i -> String.valueOf(i + 21));
}
//定義一個方法,把一個字符串轉換成int型,把int型的數據加上一個整數後,轉爲字符串輸出
private static void convert(String s, Function<String, Integer> f1, Function<Integer, String> f2) {
// Integer i = f1.apply(s);
// String ss = f2.apply(i);
// System.out.println(ss);
String apply = f1.andThen(f2).apply(s);
System.out.println(apply);
}
//定義一個方法,把一個int型數據加上一個值後轉成字符串,輸出
private static void convert(Integer i, Function<Integer, String> f) {
String result = f.apply(i);
System.out.println(result);
}
//定義一個方法,把一個字符串裝換成int型,輸出
private static void convert(String s, Function<String, Integer> f) {
Integer result = f.apply(s);
System.out.println(result);
}
}
2.Stream流
2.1 Stream流的生成方法
-
Collection體系的集合可以使用默認方法stream()生成流
default Stream stream()
-
Map體系的集合間接地生成流
-
數組可以通過Stream接口的靜態方法of(T…values)生成流
public class Demo2 {
public static void main(String[] args) {
//Collection體系可以使用默認方法stream()生成流
List<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<String>();
Stream<String> setStream = set.stream();
//Map體系的集合間接地生成流
Map<String,Integer> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<Integer> vlaueStream = map.values().stream();
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
//數組可以通過Stream接口的靜態方法of(T...values)生成流
String[] strArray = {"hello","world","java"};
Stream<String> strArray1 = Stream.of(strArray);
Stream<String> strArray2 = Stream.of("hello", "world", "java");
}
}
2.2 Stream流常見中間操作方法
2.1.1 filter(Predicate p)
Stream filter (Predicate predicate):用於對流中的數據進行過濾
Predicate 接口中的方法 Boolean test(T t):對給定的參數進行判斷,返回一個布爾值
public class Demo1 {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("張三");
names.add("張曼玉");
names.add("張無忌");
names.add("林青霞");
names.add("張開泰");
names.add("王五");
//篩選除姓張的
ArrayList<String> zhangs = new ArrayList<>();
for (String name : names) {
if(name.startsWith("張")){
zhangs.add(name);
}
}
//在姓張的基礎上篩選出姓名長度等於3的
ArrayList<String> three = new ArrayList<>();
for (String zhang : zhangs) {
if(zhang.length()==3){
three.add(zhang);
}
}
//輸出最終結果
for (String s : three) {
System.out.println(s);
}
System.out.println("----------");
//使用stream流編程
names.stream().filter(s->s.startsWith("張")).filter(s->s.length()==3).forEach(System.out::println);
/**
* names.stream(): 生成stream流對象
* filter(s->s.startsWith("張")):過濾出以“張”開頭的字符串
* filter(s->s.length()==3):在上一個條件的基礎上,過濾出名字長度爲三的字符串
* forEach(System.out::println):使用方法引用輸出最終結果
*/
}
}
2.1.2 limit(long n)
Stream limit(long n):返回截取的前 n 個元素組成的流
2.1.3 skip(long n)
Stream skip(long n):跳過指定參數個數的元素,返回剩下元素組成的流
public class Demo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("張三");
list.add("李四");
list.add("王五");
list.add("趙六");
list.add("宋七");
list.add("劉九");
//輸出前4個元素
list.stream().limit(4).forEach(System.out::println);
System.out.println("----------");
//跳過前2個,輸出剩餘的
list.stream().skip(2).forEach(System.out::println);
System.out.println("----------");
//跳過前2個,輸出剩餘元素中的前2個
list.stream().skip(2).limit(2).forEach(System.out::println);
}
}
運行結果: 張三
李四
王五
趙六
----------
王五
趙六
宋七
劉九
----------
王五
趙六
2.1.4 concat(Stream a, Stream b)
Static Stream concat(Stream a, Stream b):合併a和b兩個流爲一個流
2.1.5 distinct()
Stream distinct():返回該流的不同元素(根據 Object.equals(Object o))組成的流
public class Demo4 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("張三");
list.add("李四");
list.add("王五");
list.add("趙六");
list.add("宋七");
list.add("劉九");
//需求1:取出前4個元素組成一個流
Stream<String> stream1 = list.stream().limit(4);
//需求2:跳過前2個,剩餘的組成一個流
Stream<String> stream2 = list.stream().skip(2);
//需求3:將上面的兩個流組合成一個流,並輸出
// Stream.concat(stream1, stream2).forEach(System.out::println);
//需求4:合併需求1和需求2的流,並去除重複元素輸出
Stream.concat(stream1, stream2).distinct().forEach(System.out::println);
}
}
2.1.6 sorted()
Stream sorted(): 返回由此流的元素組成的流,自然排序
Stream sorted(Comparator comparator):返回由此流的元素組成的流,根據提供的Comparator進行排序
public class Demo5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aas");
list.add("acbds");
list.add("sccd");
list.add("cbffe");
list.add("scce");
//需求1:按照自然排序輸出
// list.stream().sorted().forEach(System.out::println);
//需求2:按照字符串長度排序
// list.stream().sorted((s1, s2) -> s1.length()-s2.length()).forEach(System.out::println);
//需求3:按照長度排序,如果長度相同,那麼再比較字母
list.stream().sorted((s1,s2)->{
int num = s1.length()-s2.length();
return num==0?s1.compareTo(s2):num;
}).forEach(System.out::println);
}
}
運行結果: aas
sccd
scce
acbds
cbffe
2.1.7 map(Function m)
Stream map(Function m): 返回由給定函數應用於此流的元素的結果組成的流
- Function 接口中的方法 apply(T t)
2.1.8 mapToInt(ToIntFunction m)
IntStream mapToInt(ToIntFunction m): 返回一個IntStream 其中包含將給定函數應用於此流的元素的結果
IntStream接口
- int sum() 返回此流中元素的總和。
public class Demo6 {
public static void main(String[] args) {
//創建一個集合
ArrayList<String> list = new ArrayList<>();
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
list.add("7");
//需求1:將字符串轉變成整數在控制檯輸出
// list.stream().map(s->Integer.parseInt(s)).forEach(System.out::println);
// list.stream().map(Integer::parseInt).forEach(System.out::println);
//int sum() 返回此流中元素的總和。
int sum = list.stream().mapToInt(Integer::parseInt).sum();
System.out.println(sum);
//list.stream()生成一個stream流
//mapToInt(Integer::parseInt) 返回一個IntStream流
//sum()調用IntStream流的特有方法求和,返回一個整型
}
}
2.3 Stream流的常見終結操作
-
void forEach(Consumer action): 遍歷元素並執行相應的操作
Consumer 接口中的方法 void accept(T t):對給定的參數執行操作
-
long count(): 返回此流中的元素個數
public class Demo7 {
public static void main(String[] args) {
ArrayList<String > list = new ArrayList<>();
list.add("林青霞");
list.add("張曼玉");
list.add("王祖賢");
list.add("趙敏");
list.add("張天愛");
//需求1:把集合中的元素輸出
// list.stream().forEach(System.out::println);
//需求2:統計集合中姓名以“張”開頭的個數,並把統計結果在控制檯輸出
long count = list.stream().filter(s -> s.startsWith("張")).count();
System.out.println(count);
}
}
運行結果: 3
綜合練習
/*
現有兩個ArrayList集合,分別存儲6名男演員名稱和女演員名稱,要求完成以下操作:
男演員只要名字爲三個字的前三人
女演員只要姓林的並且不要第一個
把過濾後的男演員姓名和女演員姓名合併到一起
把上一步操作後的元素作爲構造方法的參數創建演員對象,遍歷數據
演員類Actor已經提供,裏面有一個成員變量,一個帶參構造,以及get、set方法
*/
import java.util.ArrayList;
import java.util.stream.Stream;
class Actor {
private String name;
public String getName() {
return name;
}
@Override
public String toString() {
return "Actor{" +
"name='" + name + '\'' +
'}';
}
public void setName(String name) {
this.name = name;
}
public Actor(String name) {
this.name = name;
}
}
public class Test_02 {
public static void main(String[] args) {
ArrayList<String> maleList = new ArrayList<>();
ArrayList<String> femaleList = new ArrayList<>();
maleList.add("周潤發");
maleList.add("周星馳");
maleList.add("張國榮");
maleList.add("葛優");
maleList.add("吳京");
maleList.add("李連杰");
femaleList.add("王祖賢");
femaleList.add("林心如");
femaleList.add("林青霞");
femaleList.add("趙敏");
femaleList.add("鞏俐");
femaleList.add("林黛玉");
//男演員只要名字爲三個字的前三人
Stream<String> manStream = maleList.stream().filter(s -> s.length() == 3).limit(3);
//女演員只要姓林的並且不要第一個
Stream<String> womanStream = femaleList.stream().filter(s -> s.startsWith("林")).skip(1);
//把過濾後的男演員姓名和女演員姓名合併到一起
Stream<String> concatStream = Stream.concat(manStream, womanStream);
//把上一步操作後的元素作爲構造方法的參數創建演員對象,遍歷數據
concatStream.map(s->new Actor(s)).forEach(System.out::println);
// concatStream.map(Actor::new).forEach(a-> System.out.println(a.getName()));
}
}
運行結果: Actor{name='周潤發'}
Actor{name='周星馳'}
Actor{name='張國榮'}
Actor{name='林青霞'}
Actor{name='林黛玉'}
2.4 Stream 流的收集操作
對數據使用Stream流的方式操作完畢後,我想把流中的數據收集到集合中,該怎麼辦?
stream流的收集方法
- R collect(Collector collector)
- 但是這個收集方法的參數是一個Collector 接口
工具類Collectors 提供了具體的收集方法
- public static Collector toList(): 把元素收集到List集合中
- public static Collector toSet(): 把元素收集到Set集合中
- public static Collector toMap(Function keyMap, Function valueMap): 把元素收集到Map集合中
public class Demo8 {
public static void main(String[] args) {
//創建List集合
List<String> list = new ArrayList<>();
list.add("林青霞");
list.add("張曼玉");
list.add("王祖賢");
list.add("趙敏");
//需求1:得到名字長度爲3的流
Stream<String> lenStream = list.stream().filter(s -> s.length() == 3);
//需求2:把上一步得到的流中的數據收集到List集合中,並遍歷
List<String> names = lenStream.collect(Collectors.toList());
names.forEach(System.out::println);
//創建Set集合
Set<Integer> set = new HashSet<>();
set.add(10);
set.add(20);
set.add(30);
set.add(40);
//需求3:得到數字大於20的流
Stream<Integer> num = set.stream().filter(i -> i > 20);
//需求4:把上一步得到的流中的數據收集到Set集合中,並遍歷
Set<Integer> numStream = num.collect(Collectors.toSet());
numStream.forEach(System.out::println);
//定義一個字符串數組,每一個元素由姓名和年齡組合而成
String[] strArray = {"魍魎,12","梅花十三,20","西涼,22","精衛,18"};
//需求5:得到字符串中年齡大於18的流
Stream<String> stringStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 18);
//需求6:把上一步中得到的流存儲到Map集合中,其中名字做鍵,年齡做值
Map<String, String> student = stringStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> s.split(",")[1]));
//遍歷集合
Set<Map.Entry<String, String>> entries = student.entrySet();
entries.forEach(s-> System.out.println("姓名:"+s.getKey()+",年齡:"+s.getValue()));
}
}
運行結果: 林青霞
張曼玉
王祖賢
40
30
姓名:西涼,年齡:22
姓名:梅花十三,年齡:20