1.不可变集合
1.1 什么是不可变集合
是一个长度不可变,内容也无法修改的集合
1.2 使用场景
如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
当集合对象被不可信的库调用时,不可变形式是安全的。
简单理解:
不想让别人修改集合中的内容
1.3 不可变集合分类
一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
不可变的list集合
不可变的set集合(唯一性)
不可变的map集合(键不能重复;只能传递20个参数、10个键值对)
1.4 不可变的list集合
/*
创建不可变的List集合
"张三", "李四", "王五", "赵六"
*/
//一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
List<String> list = List.of("张三", "李四", "王五", "赵六");
//Set<String> set = Set.of("张三", "张三", "李四", "王五", "赵六");
for (String s : list) {
System.out.println(s);
}
System.out.println("---------------------------");
Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("---------------------------");
1.6 不可变的Map集合
1.6.2:键值对个数大于10
public class ImmutableDemo4 {
public static void main(String[] args) {
/*
创建Map的不可变集合,键值对的数量超过10个
*/
//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.利用上面的数据来获取一个不可变的集合
/*
//获取到所有的键值对对象(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.put("bbb","222");*/
//Map<Object, Object> map = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));
Map<String, String> map = Map.copyOf(hm);
map.put("bbb","222");
}
}
2.Stream流
2.1体验Stream流【理解】
-
案例需求
按照下面的要求完成集合的创建和遍历
-
创建一个集合,存储多个字符串元素
-
把集合中所有以"张"开头的元素存储到一个新的集合
-
把"张"开头的集合中的长度为3的元素存储到一个新的集合
-
遍历上一步得到的集合
-
-
原始方式示例代码
public class MyStream1 { public static void main(String[] args) { //集合的批量添加 ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤")); //list.add() //遍历list1把以张开头的元素添加到list2中。 ArrayList<String> list2 = new ArrayList<>(); for (String s : list1) { if(s.startsWith("张")){ list2.add(s); } } //遍历list2集合,把其中长度为3的元素,再添加到list3中。 ArrayList<String> list3 = new ArrayList<>(); for (String s : list2) { if(s.length() == 3){ list3.add(s); } } for (String s : list3) { System.out.println(s); } } }
-
使用Stream流示例代码
public class StreamDemo { public static void main(String[] args) { //集合的批量添加 ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤")); //Stream流 list1.stream().filter(s->s.startsWith("张")) .filter(s->s.length() == 3) .forEach(s-> System.out.println(s)); } }
-
Stream流的好处
-
直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印
-
Stream流把真正的函数式编程风格引入到Java中
-
代码简洁
-
2.3Stream流中间操作方法【应用】
-
概念
中间操作的意思是,执行完此方法之后,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() 返回由该流的不同元素(根据Object.equals(Object) )组成的流 -
filter代码演示
public class MyStream3 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("张三丰"); list.add("张无忌"); list.add("张翠山"); list.add("王二麻子"); list.add("张良"); list.add("谢广坤"); 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)); //林青霞 张曼玉 王祖贤 //需求2:跳过3个元素,把剩下的元素在控制台输出 list.stream().skip(3).forEach(s-> System.out.println(s)); //柳岩 张敏 张无忌 //需求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)); } }
2.4Stream流终结操作方法【应用】
-
概念
终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作
-
常见方法
方法名 说明 void forEach(Consumer action)//list.stream().forEach(xxx) 对此流的每个元素执行操作 long count()//list.stream().count() 返回此流中的元素数
2.5Stream流的收集操作【应用】
-
概念
对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中
-
常用方法
方法名 说明 R collect(Collector collector) 把结果收集到集合中 -
工具类Collectors提供了具体的收集方式
方法名 说明 public static <T> Collector toList() 把元素收集到List集合中 public static <T> Collector toSet() 把元素收集到Set集合中 public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合中 -
代码演示
//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);
3.方法引用
3.2方法引用符
-
方法引用符
:: 该符号为引用运算符,而它所在的表达式被称为方法引用
-
推导与省略
-
如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
-
如果使用方法引用,也是同样可以根据上下文进行推导
-
方法引用是Lambda的孪生兄弟
-
3.3引用类方法【应用】
引用类方法,其实就是引用类的静态方法
格式
类名::静态方法
范例
Integer::parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据
3.4引用对象的实例方法【应用】
引用对象的实例方法,其实就引用类中的成员方法
格式
对象::成员方法
范例
"HelloWorld"::toUpperCase
String类中的方法:public String toUpperCase() 将此String所有字符转换为大写
3.5引用类的实例方法【应用】
引用类的实例方法,其实就是引用类中的成员方法
格式
类名::成员方法
范例
String::substring
public String substring(int beginIndex,int endIndex)
从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex
3.6引用构造器【应用】
引用构造器,其实就是引用构造方法
-
格式
类名::new
-
范例
Student::new
-
练习描述
-
定义一个类(Student),里面有两个成员变量(name,age)
并提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法
-
定义一个接口(StudentBuilder),里面定义一个抽象方法
Student build(String name,int age);
-
定义一个测试类(StudentDemo),在测试类中提供两个方法
-
一个方法是:useStudentBuilder(StudentBuilder s)
-
一个方法是主方法,在主方法中调用useStudentBuilder方法
-
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public interface StudentBuilder { Student build(String name,int age); } public class StudentDemo { public static void main(String[] args) { //Lambda简化写法 useStudentBuilder((name,age) -> new Student(name,age)); //引用构造器 useStudentBuilder(Student::new); } private static void useStudentBuilder(StudentBuilder sb) { Student s = sb.build("林青霞", 30); System.out.println(s.getName() + "," + s.getAge()); } }
-
-
使用说明
Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数