一、不可变集合
是一个长度不可变,内容也无法修改的集合
定义完成后不可以修改,或者添加、删除
不可变集合分类
-
不可变的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>() {
@Override
public 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>() {
@Override
public 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>() {
@Override
public 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>() {
@Override
public 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>() {
@Override
public 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[]>() {
@Override
public Integer[] apply(int value) {
return new Integer[value];
}
});*/
//3.打印
}
}