集合进阶
一、创建不可变集合
不可变集合:不可以被修改的集合
(1)应用场景
①如果某个数据不能被修改,把它防御性地拷贝到不可变集合是个很哈皮的实践
②答盎集合对象被不可信的库调用时,不可变形式是安全的
简单理解:如果不想让别人修改集合中的内容,那么就可以使用不可变集合
(2)创建不可变集合的书写格式:
在List、Set、Map接口中,都存在静态的方法,可以获取一个不可变的集合
方法名称 | 说明 |
---|---|
static < E > List < E > of (E … elements) | 创建一个具有指定元素的List集合对象 |
static < E > Set < E > of (E … elements) | 创建一个具有指定元素的Set集合对象 |
static < K,V > Map < K,V > of (E … elements) | 创建一个具有指定元素的Map集合 |
**注意:**这个集合不能添加,不能删除,不能修改
①static < E > List < E > of (E … elements) 创建一个具有指定元素的List集合对象
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
public class _01_Immutable_test01 {
public static void main(String[] args) {
//创建不可变List集合 “张三”,“李四”,“王五”,“赵六”
//这个集合一旦创建完毕,是无法进行修改的,在下面的代码中,只能进行查询操作
List<String> list = List.of("张三", "李四", "王五", "赵六");
System.out.println("\n"+"--------------------------------------");
for (String s : list) {
System.out.print(s+", ");
}
System.out.println("\n"+"--------------------------------------");
Iterator<String> it = list.iterator();
while (it.hasNext()){
String str = it.next();
System.out.print(str+", ");
}
System.out.println("\n"+"--------------------------------------");
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.print(s+", ");
}
});
System.out.println("\n"+"--------------------------------------");
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
System.out.println(str+", ");
}
}
}
②static < E > Set < E > of (E … elements) 创建一个具有指定元素的Set集合对象
import java.util.Iterator;
import java.util.Set;
import java.util.function.Consumer;
public class _02_Immutable_test02 {
public static void main(String[] args) {
//创建不可变的set集合
//细节:当我们要创建以一个不可变的Set集合时,里面的参数要保证唯一性
Set<String> set = Set.of("张三", "李四", "王五", "赵六");
//一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
System.out.println("\n"+"--------------------------------------");
for (String s : set) {
System.out.print(s+", ");
}
System.out.println("\n"+"--------------------------------------");
Iterator<String> it = set.iterator();
while (it.hasNext()){
String str = it.next();
System.out.print(str+", ");
}
System.out.println("\n"+"--------------------------------------");
set.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.print(s+", ");
}
});
set.remove("lisi"); //报错:UnsupportedOperationException
}
}
③static < K,V > Map < K,V > of (E … elements) 创建一个具有指定元素的Map集合
import java.util.Map;
import java.util.Set;
public class _03_Immutable_test03 {
public static void main(String[] args) {
//创建Map不可变集合
//一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
//细节:
//1:集合里面的键不能重复
//2.Map里面的of方法,参数是有上限的,最多只能传递20个参数、10个键值对
//3.如果我们要传递多个键值对对象,超过10个,Map集合中还有一个方法 ofEntries
Map<String, Integer> map = Map.of("zhangsan", 23, "lisi", 24, "wangwu", 25, "zhaoliu", 26);
Set<String> keys = map.keySet();
for (String key : keys) {
int value = map.get(key);
System.out.println(key + "<==>" + value);
}
System.out.println("\n"+"--------------------------------------");
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + "<==>" + value);
}
}
}
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class _04_Immutable_test04 {
public static void main(String[] args) {
//如果我们有奥床架多个键值对对象的不可变Map集合,这个键值对对象超过10个,那么可以用Map集合俩面的ofEntries方法
//1.创建一个普通的Map集合
HashMap<String,String> hm = new HashMap();
hm.put("张三", "南京");
hm.put("李四", "北京");
hm.put("王五", "上海");
hm.put("赵六", "北京");
hm.put("孙七", "深圳");
hm.put("周八", "杭州");
hm.put("吴九", "宁波");
hm.put("郑十", "苏州");
hm.put("刘一", "无锡");
hm.put("陈二", "嘉兴");
hm.put("aaa", "111");
//2.利用上面的数据来获取一个不可变集合
//获取到所有的键值对对象
Set<Map.Entry<String, String>> entries = hm.entrySet();
//把entries变成一个数组
Map.Entry[] arr1 = new Map.Entry[0];
//toArray方法在底层会比较集合的长度跟数组的长度两者大小
//如果集合的长度 > 数组的长度:数据在数组中放不下,此时会根据实际数据的个数重新创建一个数组
//如果集合的长度 <= 数组的长度:数据在数组中能放下,此时不会重新创建数组,而是直接用
Map.Entry[] arr2 = entries.toArray(arr1);
//此时这个map集合就是一个不可变集合
Map map = Map.ofEntries(arr2);
}
}
//代码简化
//但是这个代码太过复杂
Map<Object, Object> objectObjectMap = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));
//第二种简化方式
//copyof方法的底层就是先判断这个集合是不是不可变集合,如果不是,就先将集合变成数组,然后利用ofEntries方法
Map<String, String> map = Map.copyOf(hm);
(3)小结
①不可变集合的特点
定义完成后不可以修改,或者添加、删除
②如何创建不可变集合
List、Set、Map接口中,都存在of方法可以创建不可变集合
③三种方式的细节
List:直接用
Set:元素不能重复
Map:元素不能重复、键值对数量不能超过10个,超过10个用ofEntries方法
二、Stream流
1.初爽stream流
import java.util.ArrayList;
public class streamTest01 {
public static void main(String[] args) {
/*
创建集合提娜佳元素,完成一下需求
1.把所有以”张“开头的元素存储到新集合当中
2.把所有以”张“开头的,长度为3元素存储到新集合当中
3.遍历打印最终结果
*/
ArrayList<String>list1 = new ArrayList<>();
list1.add("张三丰");
list1.add("周芷若");
list1.add("赵敏");
list1.add("张强");
list1.add("张无忌");
//1. 1.把所有以”张“开头的元素存储到新集合当中
ArrayList<String>list2 = new ArrayList<>();
for (String name : list1) {
if (name.startsWith("张")){
list2.add(name);
}
}
//2.把所有以”张“开头的,长度为3元素存储到新集合当中
ArrayList<String>list3 = new ArrayList<>();
for (String name : list2) {
if (name.length() == 3){
list3.add(name);
}
}
//遍历集合
for (String name : list3) {
System.out.println(name);
}
//stream流写法
list1.stream().filter(name->name.startsWith("zhang")).filter(name->name.length() == 3).forEach(name -> System.out.println(name));
}
}
2.stream流思想
(1)什么是流
相当于工厂流水线,进行各项检查个工作
(2)作用
结合Lambda表达式,简化集合、数组的操作
(3)stream流的使用步骤
先得到一条stream流(流水线),并把数据放上去
利用Stream流中的API进行各项操作
中间方法:过滤、转换 ,方法调用之后,还可以调用其他方法
终结方法:统计、打印,最后一步,调用完毕之后,不能调用其他方法
①先得到一条Stream流水线,把数据放上去
获取方式 | 方法名 | 说明 |
---|---|---|
单列集合 | default Stream < E > stream () | Collection中的默认方法 |
双列集合 | 无 | 无法直接使用Stream流 |
数组 | public static< T > Stream (T [] array) | Arrays工具类中的静态方法 |
一堆零散数据 | public static< T > of (T . . . values) | Stream接口中的静态方法 |
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.stream.Stream;
public class StreamTest02 {
public static void main(String[] args) {
/*
| 获取方式 | 方法名 | 说明 |
| ------------ | -------------------------------------- | ------------------------ |
| 单列集合 | default Stream < E > stream () | Collection中的默认方法 |
| 双列集合 | 无 | 无法直接使用Stream流 |
| 数组 | public static< T > Stream (T [] array) | Arrays工具类中的静态方法 |
| 一堆零散数据 | public static< T > of (T . . . values) | Stream接口中的静态方法 |
*/
//创建单列集合添加数据
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","c","d","e");
//获取一条流水线,并把集合的数据放到流水线上
Stream<String> stream1 = list.stream();
//使用终结方法打印流水线上的所有数据
stream1.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
/*
| 获取方式 | 方法名 | 说明 |
| ------------ | -------------------------------------- | ------------------------ |
| 双列集合 | 无 | 无法直接使用Stream流 |
*/
//1.创建一个双列集合并添加对象
HashMap<String,Integer>hm = new HashMap();
hm.put("aaa",111);
hm.put("bbb",222);
hm.put("ccc",333);
hm.put("ddd",444);
//2.获取Stream流
hm.entrySet().stream().forEach(s-> System.out.println(s));
//3.第二种获取stream
hm.entrySet().stream().forEach(s-> System.out.println(s));
/*
| 获取方式 | 方法名 | 说明 |
| ------------ | -------------------------------------- | ------------------------ |
| 数组 | public static< T > Stream (T [] array) | Arrays工具类中的静态方法 |
*/
//1.创建数组
int [] arr = {1,2,3,4,5};
String [] arr2 = {"a","b","c"};
//2.获取stream流
Arrays.stream(arr).forEach(s-> System.out.println(s));
Arrays.stream(arr2).forEach(s-> System.out.println(s));
/*
| 获取方式 | 方法名 | 说明 |
| ------------ | -------------------------------------- | ------------------------ |
| 一堆零散数据 | public static< T > of (T . . . values) | Stream接口中的静态方法 |
*/
Stream.of(1,2,3,4,5).forEach(s-> System.out.println(s));
Stream.of("a","b","c","d","e").forEach(s-> System.out.println(s));
}
}
②Stream流的中间方法
方法名称 | 说明 |
---|---|
Stream < T > filter(Predicate < ? super T > predicate) | 过滤 |
Stream < T > limit ( long maxSize ) | 获取前几个元素 |
Strean < T > skip ( long n) | 跳过前几个元素 |
Strean < T > distinct ( ) | 元素去重,依赖(hashCodehe equalsf方法) |
static < T > Stream < T > concat ( Stream a, Stream b) | 合并a和b两个流为一个流 |
Stream < R > map ( Function < T ,R > mapper) | 转换流中的数据类型 |
注意:
★中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
★修改Stream流中的数据,不会影响原来集合或者数组中的数据
import java.util.ArrayList;
import java.util.Collections;
public class StreamTest06 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","张无忌","张无忌","周芷若","赵敏","张强","张三丰","张翠山","张良","王麻子","谢广坤");
ArrayList <String> list2 = new ArrayList<>();
Collections.addAll(list,"张良","王麻子","谢广坤");
//1.Stream < T > filter(Predicate < ? super T > predicate) | 过滤
//把张开头的留下,其他的数据过滤不要
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
//如果返回值为true,表示当前数据留下
//如果返回值为false,表示当前数据舍弃不要
boolean name = s.startsWith("张");
return name;
}
}).forEach(name -> System.out.println(name));
//简写模式
list.stream().filter(s->s.startsWith("张")).forEach(s-> System.out.println(s));
//2.| Stream < T > limit ( long maxSize ) | 获取前几个元素
//这里的maxSize代表的是第几个元素,而不是索引
list.stream().limit(4).forEach(s-> System.out.println(s));
//3.| Strean < T > skip ( long n) | 跳过前几个元素
//参数是多少,就是跳过几个元素
list.stream().skip(4).forEach(s-> System.out.println(s));
//练习
list.stream().filter(s->s.startsWith("张")).skip(1).limit(3).forEach(s-> System.out.println(s));
//4.Strean < T > distinct ( ) | 元素去重,依赖(hashCodehe equalsf方法)
//如果集合或者数组的泛型是自定义的类型,就需要重写hashCode和equals方法
//这个方法在底层利用的是hashSet方法去重的
//list.stream().distinct().forEach(s-> System.out.println(s));
//5.static < T > Stream < T > concat ( Stream a, Stream b) | 合并a和b两个流为一个流
Stream<String> concat = Stream.concat(list.stream(), list2.stream());
concat.forEach(s -> System.out.println(s));
//6.Stream < R > map ( Function < T ,R > mapper) | 转换流中的数据类型
ArrayList<String> list3 = new ArrayList<>();
Collections.addAll(list3, "张无忌-15", "周芷若-14", "赵敏-13", "张强-20", "张三丰-100", "张翠山-40", "张良-35", "王二麻子-37", "谢广坤-41");
//需求:只获取里面的年龄并进行打印
//String->int
//第一个类型:流中原本的数据类型
//第二个类型:要转成之后的类型
//apply的形参s:依次表示流里面的每一个数据
//返回值:表示转换之后的数据
//当map方法执行完毕之后,流上的数据就变成了整数
//所以在下面forEach当中,s依次表示流里面的每一个数据,这个数据现在就是整数了
//第一个类型:流中原本的数据类型
//第二个类型:要转换之后的类型
//apply方法中的形参s:一次表示流里面的每一个数据
//返回值:表示转换之后的数据
//当map方法执行完毕之后,流上的数据就变成了整数,
//所以在下面forEach中,s依次表示流里面的每一个数据,但是这个数据现在是整数
list3.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s){
String[] arr = s.split("-");
String ageString = arr[1];
int age = Integer.parseInt(ageString);
return age;
}
}).forEach(s-> System.out.println(s));*/
//简写形式
list3.stream().map(s->{
String[] arr = s.split("-");
String ageString = arr[1];
int age = Integer.parseInt(ageString);
return age;
}).forEach(s-> System.out.println(s));
//===>
list3.stream().map(s->Integer.parseInt(s.split("-")[1])).forEach(s-> System.out.println(s));
}
}
③Stream流的终结方法
方法名称 | 说明 |
---|---|
void forEach (Consumer action) | 遍历 |
long count() | 统计 |
toArray() | 收集流中的数据,放到数组中 |
collect(Collector collector) | 收集流中的数据 ,放到集合中 |
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.function.IntFunction;
public class StreamTest07 {
public static void main(String[] args) {
/*
方法名称 | 说明 |
| ------------------------------ | --------------------------- |
| void forEach (Consumer action) | 遍历 |
| long count() | 统计 |
| toArray() | 收集流中的数据,放到数组中 |
| collect(Collector collector) | 收集流中的数据 ,放到集合中 |
*/
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰","张翠山","张良","王麻子","谢广坤");
//1.void forEach (Consumer action) | 遍历
//Consumer的泛型:表示流中数据的类型
//accept方法的形参s:依次表示流里面的每一个数据
//方法体:对每一个数据的处理操作(打印)
list.stream().forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//简写
list.stream().forEach(s-> System.out.println(s));
//2.long count() | 统计
long count = list.stream().count();
System.out.println(count);
//3.toArray() | 收集流中的数据,放到数组中
Object[] objects = list.stream().toArray(); //空参构造,会把数据收集到一个Object类型的数组中
System.out.println(Arrays.toString(objects));
//第二种
//IntFunction的泛型:具体类型的数组
//apply的形参:流中数据的个数,要跟数组的长度保持一致
//apply的返回值:具体类型的数组
//方法体:就是创建数组
//toArray方法的参数的作用:负责创建一个指定类型的数组
//toArray方法的底层,会依次得到流里面的每一个数据,并把数据放到数组当中
//toArray方法的返回值:是一个装着流里面所有数据的数组
String[] arr = list.stream().toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
return new String[value];
}
});
System.out.println(Arrays.toString(arr));
//简写形式
String[] arr2 = list.stream().toArray(value -> new String[value]);
System.out.println(Arrays.toString(arr2));
}
}
//收集到Map集合
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class StreamTest08 {
public static void main(String[] args) {
/*
collect(Collector collector) 收集流中的数据,放到集合中 (List Set Map)
注意点:
如果我们要收集到Map集合当中,键不能重复,否则会报错
*/
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-男-15","张无忌-男-15", "周芷若-女-14", "赵敏-女-13", "张强-男-20",
"张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");
//1.收集到List集合中
//需求:把所有的男性收集起来
List<String> newList1 = list.stream().filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toList());
System.out.println(newList1);
//收集到Set集合===》可以去除重复元素
Set<String> newList2 = list.stream().filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toSet());
System.out.println(newList2);
//3.收集Map集合当中
//谁作为键,谁作为值.
//我要把所有的男性收集起来
//键:姓名。 值:年龄
Map<String, Integer> map = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
/*
* toMap : 参数一表示键的生成规则
* 参数二表示值的生成规则
*
* 参数一:
* Function泛型一:表示流中每一个数据的类型
* 泛型二:表示Map集合中键的数据类型
*
* 方法apply形参:依次表示流里面的每一个数据
* 方法体:生成键的代码
* 返回值:已经生成的键
*
*
* 参数二:
* Function泛型一:表示流中每一个数据的类型
* 泛型二:表示Map集合中值的数据类型
*
* 方法apply形参:依次表示流里面的每一个数据
* 方法体:生成值的代码
* 返回值:已经生成的值
*
* */
.collect(Collectors.toMap(new Function<String, String>() {
@Override
public String apply(String s) {
//张无忌-男-15
return s.split("-")[0];
}
},
new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[2]);
}
}));
Map<String, Integer> map2 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(
s -> s.split("-")[0],
s -> Integer.parseInt(s.split("-")[2])));
System.out.println(map2);
}
}
(4)小结
1.Stream流的作用:
结合了Lambda表达式,简化集合、数组的操作
2.Stream流的使用步骤:
★获取Stream流对象
★使用中间方法处理数据
★使用终结方法处理数据
3.如何获取Stream流对象
★单列集合:Collection中的默认方法Stream
★双列集合:不能直接获取
★数组:Arrays工具类型中的静态方法stream
★一堆零散的数据:Stream接口中的京他爱方法of
4.常见方法
★中间方法:filter、limit、skip、distinct、concat、map
★终结方法:forEach、count、collect
3.练习
(1)定义一个集合,并添加一些数据1,2,3,4,5,6,7,8,9,10
过滤奇数,只留下偶数,并将结果保存起来
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class Test01 {
public static void main(String[] args) {
/*
定义一个集合,并添加一些整数 1,2,3,4,5,6,7,8,9,10
过滤奇数,只留下偶数。
并将结果保存起来
*/
ArrayList<Integer> list =new ArrayList<>();
Collections.addAll(list,1,2,3,4,5,6,7,8,9,10);
List<Integer> newList = list.stream().filter(s -> s % 2 == 0).collect(Collectors.toList());
System.out.println(newList);
}
}
(2)练习:
创建一个ArrayList集合,并添加以下字符串,字符串中前面是姓名,后面是年龄
“zhangsan,23”
“lisi,24”
“wangwu,25”
保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
public class Test02 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"zhangsan,23", "lisi,24","wangwu,25");
list.stream().filter(s->Integer.parseInt(s.split(",")[1])>=24)
.collect(Collectors.toMap(new Function<String, String>() {
@Override
public String apply(String s) {
return s.split(",")[0];
}
}, new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split(",")[1]);
}
}));
//简写
Map<String, Integer> map = list.stream().filter(s -> Integer.parseInt(s.split(",")[1]) >= 24)
.collect(Collectors.toMap(
s -> s.split(",")[0],
s -> Integer.parseInt(s.split(",")[1])));
System.out.println(map);
}
}
(3)现在有两个ArrayList集合,分别存储6名男演员的名字和年龄以及6名女演员的名字和年龄。
姓名和年龄中间用逗号隔开。
比如:张三,23
要求完成如下的操作:
1,男演员只要名字为3个字的前两人
2,女演员只要姓杨的,并且不要第一个
3,把过滤后的男演员姓名和女演员姓名合并到一起
4,将上一步的演员信息封装成Actor对象。
5,将所有的演员对象都保存到List集合中。
备注:演员类Actor,属性有:name,age
男演员: “蔡坤坤,24” , “叶齁咸,23”, “刘不甜,22”, “吴签,24”, “谷嘉,30”, “肖梁梁,27”
女演员: “赵小颖,35” , “杨颖,36”, “高元元,43”, “张天天,31”, “刘诗,35”, “杨小幂,33”
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test03 {
public static void main(String[] args) {
//1.创建两个ArrayList集合
ArrayList<String> manList = new ArrayList<>();
ArrayList<String> womenList = new ArrayList<>();
//2.添加数据
Collections.addAll(manList, "蔡坤坤,24", "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖梁梁,27");
Collections.addAll(womenList, "赵小颖,35", "杨颖,36", "高元元,43", "张天天,31", "刘诗,35", "杨小幂,33");
//1.男演员只要名字为3个字的前两人
Stream<String> stream1 = manList.stream()
.filter(s -> (s.split(",")[0].length() == 3))
.limit(2);
System.out.println("\n"+"----------------------------------------------");
//2.女演员只要姓杨的,并且不要第一个
Stream<String> stream2 = womenList.stream()
.filter(s->s.split(",")[0].startsWith("杨"))
.skip(1);
System.out.println("\n"+"----------------------------------------------");
//3.3,把过滤后的男演员姓名和女演员姓名合并到一起
Stream.concat(stream1, stream2);
//4.将上一步的演员信息封装成Actor对象。
Stream.concat(stream1, stream2).map(new Function<String, Actor>() {
@Override
public Actor apply(String s) {
String name = s.split(",")[0];
int age = Integer.parseInt(s.split(",")[1]);
return new Actor(name,age);
}
}).forEach(s-> System.out.println(s));
//简化书写
List<Actor> list = Stream.concat(stream1, stream2).map(s ->
new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1])))
//5.将所有的演员对象都保存到List集合中。
.collect(Collectors.toList());
System.out.println(list);
}
}
三、方法引用
1.概述
(1)方法引用:
就是把已经有的方法拿过来当作函数值接口中抽象方法的方法的方法体
(2)方法引用必须满足的条件
①引用处必须是函数式接口
②备用用的方法必须已经存在
③被用用的方法的形参和返回值需要跟抽象方法保持一致
④被引用方法的功能要满足当前的需求
import java.util.Arrays;
import java.util.Comparator;
public class FunctionTest01 {
public static void main(String[] args) {
//需求:创建一个数组,进行倒序排列
Integer[] arr = {1, 2, 3, 4, 5};
//匿名内部类
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println(Arrays.toString(arr));
//Lambda表达式
Arrays.sort(arr, (o1 , o2) -> o2 - o1);
System.out.println(Arrays.toString(arr));
//方法引用
//表示引用FunctionTest01里面的suntraction方法
//把这个方法当作抽象方法的方法体
Arrays.sort(arr,FunctionTest01::subtraction);
System.out.println(Arrays.toString(arr));
}
public static int subtraction ( int num1, int num2){
return num2 - num1;
}
}
(3)小结
①什么是方法引用?
把已经存咋爱的方法拿过来用,当作函数式接口中抽象方法的方法体
② ::是什么符号
方法引用符号
③方法引用时需要注意什么?
★引用处必须是函数式接口
★备用用的方法必须已经存在
★被用用的方法的形参和返回值需要跟抽象方法保持一致
★被引用方法的功能要满足当前的需求
2.方法引用的分类
(1)引用静态方法
① 格式:类名::静态方法
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;
public class FunctionTest02 {
public static void main(String[] args) {
//需求;集合中有以下数字,要求把他们变成int类型
//1.创建集合并添加元素
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1","2","3","4","5");
//2.常规方法
ArrayList<Integer> list2 = new ArrayList<>();
for (String s : list) {
int i = Integer.parseInt(s);
list2.add(i);
}
//Lambda表达式
list.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
}).forEach(s-> System.out.println(s));
//3.方法引用
list.stream().map(Integer::parseInt).forEach(s-> System.out.println(s));
}
}
(2)引用成员方法
格式:对象::成员方法
①引用其他类的成员方法
格式:其他类对象::方法名
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Predicate;
public class FunctionTest03 {
public static void main(String[] args) {
/*
方法引用(引用成员方法)
格式
其他类:其他类对象::方法名
本类:this::方法名(引用处不能是静态方法)
父类:super::方法名(引用处不能是静态方法)
需求:
集合中有一些名字,按照要求过滤数据
数据:"张无忌","周芷若","赵敏","张强","张三丰"
要求:只要以张开头,而且名字是3个字的
*/
//1.创建集合并添加数据
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
//2.过滤数据
list.stream()
.filter(s -> s.startsWith("张"))
.filter(s -> s.length() == 3)
.forEach(s -> System.out.println(s));
//方法引用
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("张") && s.length() == 3;
}
}).forEach(s-> System.out.println(s));
//方法引用简写
//===》 ①引用其他类的成员方法 格式:其他类对象::方法名
list.stream().filter(new StringOperation()::stringJudge);
//静态方法中是没有this的
list.stream().filter(new FunctionTest03()::stringJudge)
.forEach(s-> System.out.println(s));
}
public boolean stringJudge(String s){
return s.startsWith("张") && s.length() == 3;
}
}
②引用本类的成员方法
格式:this::方法名(引用处不能是静态方法)
③引用父类的成员方法
格式:super::方法名(引用处不能是静态方法)
(3)引用构造方法
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;
public class FunctionTest04 {
public static void main(String[] args) {
/* 方法引用(引用构造方法)
格式 类名::new
目的:创建这个类的对象
需求: 集合里面存储姓名和年龄,要求封装成Student对象并收集到List集合中
方法引用的规则:
1.需要有函数式接口
2.被引用的方法必须已经存在
3.被引用方法的形参和返回值,需要跟抽象方法的形参返回值保持一致
4.被引用方法的功能需要满足当前的需求
*/
//1.创建集合并添加元素
//1.创建集合对象
ArrayList<String> list = new ArrayList<>();
//2.添加数据
Collections.addAll(list, "张无忌,15", "周芷若,14", "赵敏,13", "张强,20", "张三丰,100", "张翠山,40", "张良,35", "王二麻子,37", "谢广坤,41");
list.stream().map(new Function<String, Student>() {
@Override
public Student apply(String s) {
String[] arr = s.split(",");
String name = arr[0];
Integer age = Integer.parseInt(arr[1]);
return new Student(name,age);
}
});
//方法引用
list.stream().map(Student::new);
}
}
//在Student的JavaBean类中添加对应的构造方法
public Student(String s){
this.name = s.split(",")[0];
this.age = Integer.parseInt(s.split(",")[1]);
}
(4)其他调用模式
①使用类名引用成员方法
格式:类名::成员方法
import java.util.ArrayList;
import java.util.Collections;
public class FunctionTest05 {
public static void main(String[] args) {
/*
方法引用(类名引用成员方法)
格式: 类名::成员方法
需求:集合里面一些字符串,要求变成大写后进行输出
方法引用的规则:
1.需要有函数式接口
2.被引用的方法必须已经存在
3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。
4.被引用方法的功能需要满足当前的需求
抽象方法形参的详解:
第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法
在Stream流当中,第一个参数一般都表示流里面的每一个数据。
假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法
第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法
局限性:
不能引用所有类中的成员方法。
是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。
*/
//1.创建集合对象
ArrayList<String> list = new ArrayList<>();
//2.添加数据
Collections.addAll(list, "aaa", "bbb", "ccc", "ddd");
//3.变成大写后进行输出
list.stream().map(new Function<String, String>() {
@Override
public String apply(String s) {
return s.toUpperCase();
}
}).forEach(s-> System.out.println(s));
//方法引用
list.stream().map(String::toUpperCase).forEach(s-> System.out.println(s));
}
}
②引用数组的构造方法
格式:数据类型::new
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.IntFunction;
public class FunctionTest06 {
public static void main(String[] args) {
/*
方法引用(数组的构造方法)
格式: 数据类型[]::new
目的:创建一个指定类型的数组
需求:集合中存储一些整数,收集到数组当中
细节:数组的类型,需要跟流中数据的类型保持一致。
*/
//1.创建集合并添加元素
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 2, 3, 4, 5);
//2.收集到数组当中
Integer[] arr = list.stream().toArray(new IntFunction<Integer[]>() {
@Override
public Integer[] apply(int value) {
return new Integer[value];
}
});
System.out.println(Arrays.toString(arr));
//方法引用
list.stream().toArray(Integer[]::new);
}
}
(5)小结
①引用静态方法
类名::静态方法
②应用成员方法
★任意类:对象::成员方法
★本类:this::成员方法
★父类:super::成员方法
③引用构造方法
类名::new
④使用类名引用成员方法
类名::成员方法(不能引用所有类中的成员方法,如果抽象方法的第一个参数时A类型的,只能引用A类中的方法)
⑤引用数组的构造方法
数据类型::new
3.练习
(1)需求:
集合中存储一些字符串的数据,比如:张三,23。
收集到Student类型的数组当中
//1.创建集合并添加元素
ArrayList list = new ArrayList<>();
Collections.addAll(list, “张无忌,15”, “周芷若,14”, “赵敏,13”, “张强,20”, “张三丰,100”, “张翠山,40”, “张良,35”, “王二麻子,37”, “谢广坤,41”);
package com_07Gather_jinjie._03Function;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Function;
public class FunctionTest07 {
public static void main(String[] args) {
/*
需求:
集合中存储一些字符串的数据,比如:张三,23。
收集到Student类型的数组当中
*/
//1.创建集合并添加元素
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌,15", "周芷若,14", "赵敏,13", "张强,20", "张三丰,100", "张翠山,40", "张良,35", "王二麻子,37", "谢广坤,41");
//2.收集到Student类型的数组当中
list.stream().map(new Function<String, Student>() {
@Override
public Student apply(String s) {
return new Student(s.split(",")[0],Integer.parseInt(s.split(",")[1]));
}
}).toArray(Student[]::new);
//方法引用
Student[] arr = list.stream().map(Student::new).toArray(Student[]::new);
System.out.println(Arrays.toString(arr));
}
}
(2)需求:
- 创建集合添加学生对象
- 学生对象属性:name,age
- 要求:
- 获取姓名并放到数组当中
- 使用方法引用完成
import java.util.ArrayList;
import java.util.function.Function;
public class FunctionTest08 {
public static void main(String[] args) {
/*
* 需求:
* 创建集合添加学生对象
* 学生对象属性:name,age
* 要求:
* 获取姓名并放到数组当中
* 使用方法引用完成
*
* 技巧:
* 1.现在有没有一个方法符合我当前的需求
* 2.如果有这样的方法,这个方法是否满足引用的规则
* 静态 类名::方法名
* 成员方法
* 构造方法 类名::new
* */
//1.创建集合
ArrayList<Student> list = new ArrayList<>();
//2.添加元素
list.add(new Student("zhangsan",23));
list.add(new Student("lisi",24));
list.add(new Student("wangwu",25));
list.stream().map(new Function<Student, String>() {
@Override
public String apply(Student student) {
return student.getName();
}
}).toArray(String[]::new);
//方法引用
list.stream().map(Student::getName).toArray(String[]::new);
}
}
(3)
需求:
创建集合添加学生对象
学生对象属性:name,age
要求:
把姓名和年龄拼接成:张三-23的字符串,并放到数组当中
使用方法引用完成
import java.util.ArrayList;
import java.util.Arrays;
public class FunctionTest09 {
public static void main(String[] args) {
/*
* 需求:
* 创建集合添加学生对象
* 学生对象属性:name,age
* 要求:
* 把姓名和年龄拼接成:张三-23的字符串,并放到数组当中
* 使用方法引用完成
* */
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("zhangsan",23));
list.add(new Student("lisi",24));
list.add(new Student("wangwu",25));
/*list.stream().map(new Function<Student, String>() {
@Override
public String apply(Student student) {
return student.getName()+","+student.getAge();
}
}).toArray(String[]::new);*/
String[] arr = list.stream().map(Student::toString).toArray(String[]::new);
System.out.println(Arrays.toString(arr));
}
}
//重写Student里面的toString方法
public String toString() {
return this.getName()+","+this.getAge();
}