【Java】不可变集合

一、创建不可变集合

不可变集合:不可以被修改的集合。

一旦创建完毕后,长度不能改,内容也不能改。


二、创建不可变集合 的应用场景

如果某个数据不能被修改,把它防御性拷贝到不可变集合中是个很好的实际。

当集合对象被不可信的库调用时,不可变形式是安全的。

简单理解:如果你不想让别人去修改集合中的内容,那么就可以给它去提供一个不可变的集合。

当别人拿到这个不可变的集合后,它只能进行查询操作,是不能修改 / 删除 / 添加的。

这样的场景有很多,例如斗地主游戏中的牌盒,牌盒中装了所有的牌,这些牌是固定的,一共 54张

我不想让别人去修改,那万一别人加个 3 进去,那就是出老千了。

因此为了防止别人出老千,我们就可以使用不可变的集合

image-20240429213634472

还有斗地主游戏中的出牌规则,这些规则不想增加,也不想删除,也不想让别人修改,也可以使用不可变集合。

image-20240429213932604

还有用代码获取的操作系统硬件信息,也是不能被修改的。

总而言之,只要你不想让别人去修改集合中的数据,只想让别人进行查询操作的话,这些数据就可以放到不可变集合中。


三、创建不可变集合的书写格式

ListSetMap 接口中,都存在静态的 of方法,可以获取一个不可变的集合。

image-20240429214446004


四、创建不可变的 List集合

可以发现这个方法的形参是个可变参数,因此在这个方法里面我们可以传递很多数据。

image-20240429215654401
//一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
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");
}

五、创建不可变的 Set集合

可以发现,它的形参跟刚刚的 List.of() 是一模一样的,也是一个可变参数,因此我们可以往小括号中传递很多信息。

image-20240429215920381

细节:当我们要获取一个不可变的Set集合时,里面的参数一定要保证唯一性,否则就会报错

image-20240430065645597
//一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
Set<String> set = Set.of("张三", "张三", "李四", "王五", "赵六");

//由于Set集合是没有索引的,因此如果我们想要查询,只能遍历
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("王五");

六、创建不可变的 Map集合

Java会把第一个参数和第二个参数认为是一个键值对对象,第三个参数和第四个参数认为是一个键值对对象…

image-20240430065852710

细节1:键是不能重复的,如果重复就会报错。


细节2:Map里面的of方法,参数是有上限的,最多只能传递20个参数,即10个键值对。

ListSet 能传递多个,就是因为它的形参是 可变参数

但是 Map,选中 Map 跟进,搜索 of(),可以发现它并没有带可变参数的 of()

image-20240430070632698

参数最多的就是 20个参数

image-20240430070723205

那为什么它没有设计带有可变参数的方法呢?我们自己来写个方法试试。

如果我们想让 Mapof方法 也能接收多个键和值,那么键和值都需要写成可变参数。

但键和值的类型不确定,因此可以使用泛型方法来解决,写完就会发现立马报错:Vararg parameter must be the last in the list(必须在形参的最后)。

image-20240430071224132
//一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
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("--------------------------");
}

细节3:如果我们要传递多个键值对对象,数量大于10个,在Map接口中还有一个方法。

我们可以将 键值 看做是一个整体,传递给 ofEntries(),这个方法就可以给你返回一个不可变集合。

image-20240430071438703
//创建一个普通的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");

利用上面的数据来获取一个不可变的集合,首先需要获取到所有的键值对对象(Entry对象)

Set<Map.Entry<String, String>> entries = hm.entrySet();

由于ofEntries方法的形参是一个可变参数,可变参数实际上就是一个数组,因此我们需要把entries变成一个数组。

我们可以用 entries 调用里面的 toArray()to:变成,array:数组

首先来调用一下没有参数的 toArray(),没有参数就表示没有指定类型,那么它返回的就是 Object类型 的。

但此时我们想要给这个数组指定类型,就可以使用下面的第二个带形参的方法,我们需要对这个数组指定类型。

image-20240430072817743
//toArray方法在底层会比较集合的长度跟数组的长度两者的大小
//如果集合的长度 > 数组的长度 :数据在数组中放不下,此时会根据实际数据的个数,重新创建数组
//如果集合的长度 <= 数组的长度:数据在数组中放的下,此时不会创建新的数组,而是直接用
//假设数组初始化长度为20,11个键值对会填满前11个位置,后面的9个默认初始化值为null
//因此数组的长度直接写0就行了,我们不用去关心它的长度,因为底层它会自己判断8
Map.Entry[] arr = entries.toArray(new Map.Entry[0]);
//不可变的map集合
//可变参数实际上就是一个数组,因此我们直接将数组传递过去也是可以的
Map map = Map.ofEntries(arr);
map.put("bbb","222"); // 这行代码会报错

上面这种写法代码太多太麻烦了,我们试着将它简化一下

Map<Object, Object> map = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));

Map.copyOf()

但是这样写还是太麻烦了,因此Java给我们在 Map接口 中还提供了一个 copyOf()

在这个方法中它会做一个判断

static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
    // 当你传过来的 Map集合 就是一个不可变的集合,那么它直接把本身的集合给你返回
    if (map instanceof ImmutableCollections.AbstractImmutableMap) {
        return (Map<K,V>)map;
    } else { // 但如果你传递过来的 Map集合 是可变的,这段代码就跟我们刚刚写的一模一样
        return (Map<K,V>)Map.ofEntries(map.entrySet().toArray(new Entry[0]));
    }
}

因此在以后,如果我们要生成不可变的 Map集合 直接使用 copyOf 就行了,只不过它是在 JDK10 的时候才出现的。

image-20240430074351587
Map<String, String> map = Map.copyOf(hm);
map.put("bbb","222"); // 报错

七、总结

1、不可变集合的特点?

定义完成后只能查询,不能修改 / 添加 / 删除

2、如何创建不可变集合?

ListSetMap 接口中,都存在静态的 of() 可以创建不可变集合

3、三种方式的细节

  • List:直接用

  • Set:元素不能重复

  • Map:元素不能重复、键值对数量最多是10个,超过10个用 ofEntries方法

    如果你的JDK版本大于等于10,可以用 copyOf() 简化代码

  • 13
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值