1.不可变集合
1.1 什么是不可变集合
是一个长度不可变,内容也无法修改的集合
1.2 使用场景
如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
当集合对象被不可信的库调用时,不可变形式是安全的。
简单理解:
不想让别人修改集合中的内容
比如说:
1,斗地主的54张牌,是不能添加,不能删除,不能修改的
2,斗地主的打牌规则:单张,对子,三张,顺子等,也是不能修改的
3,用代码获取的操作系统硬件信息,也是不能被修改的
1.3 不可变集合分类
- 不可变的list集合
- 不可变的set集合
- 不可变的map集合
1.4 不可变的list集合
public class ImmutableDemo1 { public static void main(String[] args) { /* 创建不可变的List集合 "张三", "李四", "王五", "赵六" */ //一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作 List<String> list = List.of("张三", "李四", "王五", "赵六"); System.out.println(list.get(0)); System.out.println(list.get(1)); System.out.println(list.get(2)); System.out.println(list.get(3)); System.out.println("---------------------------"); 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("---------------------------"); for (int i = 0; i < list.size(); i++) { String s = list.get(i); System.out.println(s); } System.out.println("---------------------------"); //list.remove("李四"); //list.add("aaa"); list.set(0,"aaa"); } }
1.5 不可变的Set集合
public class ImmutableDemo2 { public static void main(String[] args) { /* 创建不可变的Set集合 "张三", "李四", "王五", "赵六" 细节: 当我们要获取一个不可变的Set集合时,里面的参数一定要保证唯一性 */ //一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作 Set<String> set = Set.of("张三", "张三", "李四", "王五", "赵六"); for (String s : set) { System.out.println(s); } System.out.println("-----------------------"); Iterator<String> it = set.iterator(); while(it.hasNext()){ String s = it.next(); System.out.println(s); } System.out.println("-----------------------"); //set.remove("王五"); } }
1.6 不可变的Map集合
1.6.1:键值对个数小于等于10
public class ImmutableDemo3 { public static void main(String[] args) { /* 创建Map的不可变集合 细节1: 键是不能重复的 细节2: Map里面的of方法,参数是有上限的,最多只能传递20个参数,10个键值对 细节3: 如果我们要传递多个键值对对象,数量大于10个,在Map接口中还有一个方法 */ //一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作 Map<String, String> map = Map.of("张三", "南京", "张三", "北京", "王五", "上海", "赵六", "广州", "孙七", "深圳", "周八", "杭州", "吴九", "宁波", "郑十", "苏州", "刘一", "无锡", "陈二", "嘉兴"); Set<String> keys = map.keySet(); for (String key : keys) { String value = map.get(key); System.out.println(key + "=" + value); } System.out.println("--------------------------"); Set<Map.Entry<String, String>> entries = map.entrySet(); for (Map.Entry<String, String> entry : entries) { String key = entry.getKey(); String value = entry.getValue(); System.out.println(key + "=" + value); } System.out.println("--------------------------"); } }
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.方法引用
2.1体验方法引用
- 方法引用的出现原因
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作
那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定是没有必要
那我们又是如何使用已经存在的方案的呢?
这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案 - 代码演示
public interface Printable { void printString(String s); } public class PrintableDemo { public static void main(String[] args) { //在主方法中调用usePrintable方法 // usePrintable((String s) -> { // System.out.println(s); // }); //Lambda简化写法 usePrintable(s -> System.out.println(s)); //方法引用 usePrintable(System.out::println); } private static void usePrintable(Printable p) { p.printString("爱生活爱Java"); } }
2.2方法引用符
- 方法引用符
:: 该符号为引用运算符,而它所在的表达式被称为方法引用 - 推导与省略
- 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用是Lambda的孪生兄弟
2.3引用类方法
引用类方法,其实就是引用类的静态方法
- 格式
类名::静态方法 - 范例
Integer::parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据 - 练习描述
- 定义一个接口(Converter),里面定义一个抽象方法 int convert(String s);
- 定义一个测试类(ConverterDemo),在测试类中提供两个方法
- 一个方法是:useConverter(Converter c)
- 一个方法是主方法,在主方法中调用useConverter方法
- 代码演示
public interface Converter { int convert(String s); } public class ConverterDemo { public static void main(String[] args) { //Lambda写法 useConverter(s -> Integer.parseInt(s)); //引用类方法 useConverter(Integer::parseInt); } private static void useConverter(Converter c) { int number = c.convert("666"); System.out.println(number); } }
- 使用说明
Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
2.4引用对象的实例方法
引用对象的实例方法,其实就引用类中的成员方法
- 格式
对象::成员方法 - 范例
“HelloWorld”::toUpperCase
String类中的方法:public String toUpperCase() 将此String所有字符转换为大写 - 练习描述
- 定义一个类(PrintString),里面定义一个方法
public void printUpper(String s):把字符串参数变成大写的数据,然后在控制台输出 - 定义一个接口(Printer),里面定义一个抽象方法
void printUpperCase(String s) - 定义一个测试类(PrinterDemo),在测试类中提供两个方法
- 一个方法是:usePrinter(Printer p)
- 一个方法是主方法,在主方法中调用usePrinter方法
- 代码演示
public class PrintString { //把字符串参数变成大写的数据,然后在控制台输出 public void printUpper(String s) { String result = s.toUpperCase(); System.out.println(result); } } public interface Printer { void printUpperCase(String s); } public class PrinterDemo { public static void main(String[] args) { //Lambda简化写法 usePrinter(s -> System.out.println(s.toUpperCase())); //引用对象的实例方法 PrintString ps = new PrintString(); usePrinter(ps::printUpper); } private static void usePrinter(Printer p) { p.printUpperCase("HelloWorld"); } }
- 使用说明
Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数
2.5引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法
- 格式
类名::成员方法 - 范例
String::substring
public String substring(int beginIndex,int endIndex)
从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex - 练习描述
- 定义一个接口(MyString),里面定义一个抽象方法:
String mySubString(String s,int x,int y); - 定义一个测试类(MyStringDemo),在测试类中提供两个方法
- 一个方法是:useMyString(MyString my)
- 一个方法是主方法,在主方法中调用useMyString方法
- 代码演示
public interface MyString { String mySubString(String s,int x,int y); } public class MyStringDemo { public static void main(String[] args) { //Lambda简化写法 useMyString((s,x,y) -> s.substring(x,y)); //引用类的实例方法 useMyString(String::substring); } private static void useMyString(MyString my) { String s = my.mySubString("HelloWorld", 2, 5); System.out.println(s); } }
- 使用说明
Lambda表达式被类的实例方法替代的时候
第一个参数作为调用者
后面的参数全部传递给该方法作为参数
2.6引用构造器
引用构造器,其实就是引用构造方法
- l格式
类名::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表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数
后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹