一、不可变集合
是一个长度不可变,内容也无法修改的集合
定义完成后不可以修改,或者添加、删除
不可变集合分类
-
不可变的list集合
-
不可变的set集合
-
不可变的map集合
如何创建不可变集合?
List、Set、Map接口中,都存在of方法可以创建不可变集合
三种方式的细节
- List:直接用
- Set:元素不能重复
- Map:元素不能重复、键值对数量最多是10个。
超过10个用ofEntries方法,JDK10以后出了一个Map.copyof(hm)方法,直接就可以使用了
List<String> list = List.of("张三", "李四", "王五", "赵六");
Set<String> set = Set.of("张三", "张三", "李四", "王五", "赵六"); Set<String> set = Set.of("张三", "张三", "李四", "王五", "赵六");
Map<String, String> map = Map.of("张三", "南京", "张三", "北京", "王五", "上海","赵六", "广州", "孙七", "深圳", "周八", "杭州","吴九", "宁波", "郑十", "苏州", "刘一", "无锡","陈二", "嘉兴");
//获取到所有的键值对对象(Entry对象)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> map = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));//或者
Map<String, String> map = Map.copyOf(hm);
二、Stream流
结合Lambda表达式,简化集合数组的操作
使用步骤:
1.先得到一条Stream流(流水线),并把数据放上去
2.利用Stream流中的API进行各种操作
过滤、转换 中间方法 方法调用完毕之后,还可以调用其他方法
统计、打印 终结方法 最后一步,调用完毕之后,不能调用其他方法
2.使用中间方法对流水线上的数据进行操作
3.使用终结方法对流水线上的数据进行操作
1.获取Stream流
获取方式 | 方法名 | 说明 |
单列集合 | default Stream<E> stream() | collection中的默认方法 |
双列集合 | 先使用keySet () entrySet () 获取单列集合 | 无法直接使用stream流 |
数组 | public static <T> Stream<T> stream(T[]array) | Arrays工具类中的静态方法 |
一堆零散数据 | public static<T> Stream<T> of(T... values) | Stream接口中的静态方法 |
public class StreamDemo {public static void main(String[] args) {//Collection体系的集合可以使用默认方法stream()生成流List<String> list = new ArrayList<String>();Stream<String> listStream = list.stream();Set<String> set = new HashSet<String>();Stream<String> setStream = set.stream();//Map体系的集合间接的生成流Map<String,Integer> map = new HashMap<String, Integer>();Stream<String> keyStream = map.keySet().stream();Stream<Integer> valueStream = map.values().stream();Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();//数组可以通过Arrays中的静态方法stream生成流String[] strArray = {"hello","world","java"};Stream<String> strArrayStream = Arrays.stream(strArray);//同种数据类型的多个数据可以通过Stream接口的静态方法of(T... values)生成流Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");Stream<Integer> intStream = Stream.of(10, 20, 30);}
}
2.Stream流中间操作方法
方法名 | 说明 |
---|---|
Stream<T> filter(Predicate predicate) | 过滤 |
Stream<T> limit(long maxSize) | 获取前几个元素 |
Stream<T> skip(long n) | 跳过前几个元素 |
static <T> Stream<T> concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
Stream<T> distinct() | 元素去重,依赖(hashCode和equals方法) |
Stream<R> map(Function<T , R> mapper) | 转换流中的数据类型 |
注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据
filter代码演示
public class MyStream3 {public static void main(String[] args) {
// Stream<T> filter(Predicate predicate):过滤
// Predicate接口中的方法 boolean test(T t):对给定的参数进行判断,返回一个布尔值ArrayList<String> list = new ArrayList<>();list.add("张三丰");list.add("张无忌");list.add("张翠山");list.add("王二麻子");list.add("张良");list.add("谢广坤");//filter方法获取流中的 每一个数据.//而test方法中的s,就依次表示流中的每一个数据.//我们只要在test方法中对s进行判断就可以了.//如果判断的结果为true,则当前的数据留下//如果判断的结果为false,则当前数据就不要.
// list.stream().filter(
// new Predicate<String>() {
// @Override
// public boolean test(String s) {
// boolean result = s.startsWith("张");
// return result;
// }
// }
// ).forEach(s-> System.out.println(s));//因为Predicate接口中只有一个抽象方法test//所以我们可以使用lambda表达式来简化
// list.stream().filter(
// (String s)->{
// boolean result = s.startsWith("张");
// return result;
// }
// ).forEach(s-> System.out.println(s));list.stream().filter(s ->s.startsWith("张")).forEach(s-> System.out.println(s));}
}
limit&skip代码演示
public class StreamDemo02 {public static void main(String[] args) {//创建一个集合,存储多个字符串元素ArrayList<String> list = new ArrayList<String>();list.add("林青霞");list.add("张曼玉");list.add("王祖贤");list.add("柳岩");list.add("张敏");list.add("张无忌");//需求1:取前3个数据在控制台输出list.stream().limit(3).forEach(s-> System.out.println(s));System.out.println("--------");//需求2:跳过3个元素,把剩下的元素在控制台输出list.stream().skip(3).forEach(s-> System.out.println(s));System.out.println("--------");//需求3:跳过2个元素,把剩下的元素中前2个在控制台输出list.stream().skip(2).limit(2).forEach(s-> System.out.println(s));}
}
concat&distinct代码演示
public class StreamDemo03 {public static void main(String[] args) {//创建一个集合,存储多个字符串元素ArrayList<String> list = new ArrayList<String>();list.add("林青霞");list.add("张曼玉");list.add("王祖贤");list.add("柳岩");list.add("张敏");list.add("张无忌");//需求1:取前4个数据组成一个流Stream<String> s1 = list.stream().limit(4);//需求2:跳过2个数据组成一个流Stream<String> s2 = list.stream().skip(2);//需求3:合并需求1和需求2得到的流,并把结果在控制台输出
// Stream.concat(s1,s2).forEach(s-> System.out.println(s));//需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复Stream.concat(s1,s2).distinct().forEach(s-> System.out.println(s));}
}
3.Stream流终结操作方法
方法名 | 说明 |
---|---|
void forEach(Consumer action) | 遍历 |
long count() | 统计个数 |
toArray() | 收集流中数据,放到数组中 |
collect(Collector collector) | 收集流中数据,放到集合中 |
public class MyStream5 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("张三丰");list.add("张无忌");list.add("张翠山");list.add("王二麻子");list.add("张良");list.add("谢广坤");//method1(list);// long count():返回此流中的元素数long count = list.stream().count();System.out.println(count);}private static void method1(ArrayList<String> list) {// void forEach(Consumer action):对此流的每个元素执行操作// Consumer接口中的方法void accept(T t):对给定的参数执行此操作//在forEach方法的底层,会循环获取到流中的每一个数据.//并循环调用accept方法,并把每一个数据传递给accept方法//s就依次表示了流中的每一个数据.//所以,我们只要在accept方法中,写上处理的业务逻辑就可以了.list.stream().forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}});System.out.println("====================");//lambda表达式的简化格式//是因为Consumer接口中,只有一个accept方法list.stream().forEach((String s)->{System.out.println(s);});System.out.println("====================");//lambda表达式还是可以进一步简化的.list.stream().forEach(s->System.out.println(s));}
}
collection应用:
// toList和toSet方法演示
public class MyStream7 {public static void main(String[] args) {ArrayList<Integer> list1 = new ArrayList<>();for (int i = 1; i <= 10; i++) {list1.add(i);}list1.add(10);list1.add(10);list1.add(10);list1.add(10);list1.add(10);//filter负责过滤数据的.//collect负责收集数据.//获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中.//Collectors.toList() : 在底层会创建一个List集合.并把所有的数据添加到List集合中.List<Integer> list = list1.stream().filter(number -> number % 2 == 0).collect(Collectors.toList());System.out.println(list);Set<Integer> set = list1.stream().filter(number -> number % 2 == 0).collect(Collectors.toSet());System.out.println(set);
}
}
/**
Stream流的收集方法 toMap方法演示
创建一个ArrayList集合,并添加以下字符串。字符串中前面是姓名,后面是年龄
"zhangsan,23"
"lisi,24"
"wangwu,25"
保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值
*/
public class MyStream8 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("zhangsan,23");list.add("lisi,24");list.add("wangwu,25");Map<String, Integer> map = list.stream().filter(s -> {String[] split = s.split(",");int age = Integer.parseInt(split[1]);return age >= 24;}// collect方法只能获取到流中剩余的每一个数据.//在底层不能创建容器,也不能把数据添加到容器当中//Collectors.toMap 创建一个map集合并将数据添加到集合当中// s 依次表示流中的每一个数据//第一个lambda表达式就是如何获取到Map中的键//第二个lambda表达式就是如何获取Map中的值).collect(Collectors.toMap(s -> s.split(",")[0],s -> Integer.parseInt(s.split(",")[1]) ));System.out.println(map);}
}
总结:
1. Stream流的作用
结合了Lambda表达式,简化集合、数组的操作
2. Stream的使用步骤
获取Stream流对象使用中间方法处理数据●使用终结方法处理数据
3.如何获取Stream流对象
- 单列集合:Collection中的默认方法stream
- 双列集合:不能直接获取
- 数组: Arrays工具类型中的静态方法stream
- 一堆零散的数据:Stream接口中的静态方法of
4.常见方法
中间方法:
filter,limit,skip, distinct,concat,map
终结方法:
forEach,count,collect
三、方法引用
1.什么是方法引用?
把已经存在的方法拿过来用,当做函数式接口中抽象方法的方法体
2.︰︰是什么符号?
方法引用符
3.方法引用时要注意什么?
- 需要有函数式接口
- 被引用方法必须已经存在
- 被引用方法的形参和返回值需要跟抽象方法保持一致
- 被引用方法的功能要满足当前的需求
4.方法引用的分类
- 引用静态方法
- 引用成员方法
- 引用其他类的成员方法
- 引用本类的成员方法
- 引用父类的成员方
3.引用构造方法
4.其他调用方式
- 使用类名引用成员方法
- 引用数组的构造方法
1.引用静态方法
类名::静态方法
package com.itheima.a01myfunction;import java.util.ArrayList;
import java.util.Collections;public class FunctionDemo2 {public static void main(String[] args) {/*方法引用(引用静态方法)格式类::方法名需求:集合中有以下数字,要求把他们都变成int类型"1","2","3","4","5"*///1.创建集合并添加元素ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"1","2","3","4","5");//2.把他们都变成int类型/* list.stream().map(new Function<String, Integer>() {@Overridepublic Integer apply(String s) {int i = Integer.parseInt(s);return i;}}).forEach(s -> System.out.println(s));*///1.方法需要已经存在//2.方法的形参和返回值需要跟抽象方法的形参和返回值保持一致//3.方法的功能需要把形参的字符串转换成整数list.stream().map(Integer::parseInt).forEach(s-> System.out.println(s));}
}
2.引用成员方法
对象∶:成员方法
this: :成员方法
super: :成员方法
package com.itheima.a01myfunction;import java.util.ArrayList;
import java.util.Collections;public class FunctionDemo3 {public static void main(String[] args) {/*方法引用(引用成员方法)格式其他类:其他类对象::方法名本类:this::方法名(引用处不能是静态方法)父类:super::方法名(引用处不能是静态方法)需求:集合中有一些名字,按照要求过滤数据数据:"张无忌","周芷若","赵敏","张强","张三丰"要求:只要以张开头,而且名字是3个字的*///1.创建集合ArrayList<String> list = new ArrayList<>();//2.添加数据Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");//3.过滤数据(只要以张开头,而且名字是3个字的)//list.stream().filter(s->s.startsWith("张")).filter(s->s.length() == 3).forEach(s-> System.out.println(s));/* list.stream().filter(new Predicate<String>() {@Overridepublic boolean test(String s) {return s.startsWith("张") && s.length() == 3;}}).forEach(s-> System.out.println(s));*//* StringOperation so = new StringOperation();list.stream().filter(so::stringJudge).forEach(s-> System.out.println(s));*///静态方法中是没有this的list.stream().filter(new FunctionDemo3()::stringJudge).forEach(s-> System.out.println(s));}public boolean stringJudge(String s){return s.startsWith("张") && s.length() == 3;}
}
3.引用构造方法
类名: : new
package com.itheima.a01myfunction;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;public class FunctionDemo4 {public static void main(String[] args) {/*方法引用(引用构造方法)格式类名::new目的:创建这个类的对象需求:集合里面存储姓名和年龄,要求封装成Student对象并收集到List集合中方法引用的规则:1.需要有函数式接口2.被引用的方法必须已经存在3.被引用方法的形参和返回值,需要跟抽象方法的形参返回值保持一致4.被引用方法的功能需要满足当前的需求*///1.创建集合对象ArrayList<String> list = new ArrayList<>();//2.添加数据Collections.addAll(list, "张无忌,15", "周芷若,14", "赵敏,13", "张强,20", "张三丰,100", "张翠山,40", "张良,35", "王二麻子,37", "谢广坤,41");//3.封装成Student对象并收集到List集合中//String --> Student/* List<Student> newList = list.stream().map(new Function<String, Student>() {@Overridepublic Student apply(String s) {String[] arr = s.split(",");String name = arr[0];int age = Integer.parseInt(arr[1]);return new Student(name, age);}}).collect(Collectors.toList());System.out.println(newList);*/List<Student> newList2 = list.stream().map(Student::new).collect(Collectors.toList());System.out.println(newList2);}
}
4.使用类名引用成员方法
类名::成员方法
package com.itheima.a01myfunction;import java.util.ArrayList;
import java.util.Collections;public class FunctionDemo5 {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.变成大写后进行输出//map(String::toUpperCase)//拿着流里面的每一个数据,去调用String类中的toUpperCase方法,方法的返回值就是转换之后的结果。list.stream().map(String::toUpperCase).forEach(s -> System.out.println(s));//String --> String/* list.stream().map(new Function<String, String>() {@Overridepublic String apply(String s) {return s.toUpperCase();}}).forEach(s -> System.out.println(s));*/}
}
5.引用数组的构造方法
数据类型::new
package com.itheima.a01myfunction;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;public class FunctionDemo6 {public static void main(String[] args) {/*方法引用(数组的构造方法)格式数据类型[]::new目的:创建一个指定类型的数组需求:集合中存储一些整数,收集到数组当中细节:数组的类型,需要跟流中数据的类型保持一致。*///1.创建集合并添加元素ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list, 1, 2, 3, 4, 5);//2.收集到数组当中Integer[] arr2 = list.stream().toArray(Integer[]::new);System.out.println(Arrays.toString(arr2));/*Integer[] arr = list.stream().toArray(new IntFunction<Integer[]>() {@Overridepublic Integer[] apply(int value) {return new Integer[value];}});*///3.打印}
}