List、Set、Map 的 of() 方法

JDK9在 List、Set、Map 等,都提供了 of() 方法,表面上看来,它们似乎只是建立 List、Set、Map 实例的便捷方法,例如:

List<String> nameLt = List.of("Justin", "Monica");

nameLt ==> [Justin, Monica]


Set<String> nameSet = Set.of("Justin", "Monica");

nameSet ==> [Monica, Justin]


Map<String, Integer> scoreMap = Map.of("Justin", 95, "Monica", 100);

scoreMap ==> {Justin=95, Monica=100}

比较特别的是 Map.of(),它是采取 Map.of(K1,V1,K2,V2) 的方式建立,也就是键、值、键值的方式来指定。List、Set、Map 的 of() 方法建立的是不可变的,不可添加新的元素,否则会抛出 UnsupportedOperationException,例如:

nameLt.add("Irene");
|  java.lang.UnsupportedOperationException thrown:
|        at ImmutableCollections.uoe (ImmutableCollections.java:70)
|        at ImmutableCollections$AbstractImmutableList.add (ImmutableCollections.java:76)
|        at (#5:1)


nameSet.add("Irene");
|  java.lang.UnsupportedOperationException thrown:
|        at ImmutableCollections.uoe (ImmutableCollections.java:70)
|        at ImmutableCollections$AbstractImmutableSet.add (ImmutableCollections.java:280)
|        at (#6:1)


scoreMap.put("Irene", 100);
|  java.lang.UnsupportedOperationException thrown:
|        at ImmutableCollections.uoe (ImmutableCollections.java:70)
|        at ImmutableCollections$AbstractImmutableMap.put (ImmutableCollections.java:557)
|        at (#7:1)

那么可以避免方才Collections的unmodifiableXXX()上提到之问题吗?这些of()方法多数都是采可变长度参数的方式定义,而是重载了多个不同参数个数的版本,以List的of()方法为例:

在参数少于10个的情况下,会使用对应个数的of()版本,因而不会有参考原List实例的问题,至于那个of(E…elements)版本,内部并不会直接参考原本elements参考的实例,而是建立一个新数组,然后对elements的元素逐一浅层复制,底下列出JDK中的原始码实作片段以便了解:

ListN(E... input) {
    // copy and check manually to avoid TOCTOU
    @SuppressWarnings("unchecked")
    E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
    for (int i = 0; i < input.length; i++) {
        tmp[i] = Objects.requireNonNull(input[i]);
    }
    this.elements = tmp;
}

 因此在数据结构上,就算你对该版本的of()方法直接传入数组,也没有参考至原elements参考之物件的疑虑,从而更进一步支持了不可变特性,然而要注意,因为是元素是浅层复制,如果你直接变更了元素的状态,of()方法传回的物件还是会反应出对应的状态变更。例如:

 class Student {
   ...>     String name;
   ...> }
|  created class Student

 Student student = new Student();
student ==> Student@cb644e

 student.name = "Justin";
$3 ==> "Justin"

 List<Student> students = List.of(student);
students ==> [Student@cb644e]

 students.get(0).name;
$5 ==> "Justin"

 student.name = "Monica";
$6 ==> "Monica"

 students.get(0).name;
$7 ==> "Monica"

 以上面的程序片段来说,如果你想要更进一步的不可变特性,应该令Student类别在定义时也支持不可变特性,如此一来,使用List.of()方法才有意义,例如

 class Student {
   ...>     final String name;
   ...>     Student(String name) {
   ...>         this.name = name;
   ...>     }
   ...> }
|  created class Student

 Student student = new Student("Justin");
student ==> Student@cb644e

 List<Student> students = List.of(student);
students ==> [Student@cb644e]

 你也许会想到Arrays.asList()方法,似乎与List.of()方法很像,Arrays.asList()方法传回的物件长度固定,确实也是无法修改,由于方法定义时使用不定长度参数,也可以直接指定数组作为引数,这就会引发类似的问题:

 String[] names = {"Justin", "Monica"};
names ==> String[2] { "Justin", "Monica" }

 List<String> nameLt = Arrays.asList(names);
nameLt ==> [Justin, Monica]

 names[0] = "Irene";
$3 ==> "Irene"

 nameLt;
nameLt ==> [Irene, Monica]

会发生这个问题的理由类似,Arrays.asList()传回的物件,内部参考了names参考之物件(你可以试着查看Arrays.java的原始码实作来验证);如果你需要的是不可变物件,而不是无法修改的物件,那么在JDK9之后,建议改用List.of(),而不是Arrays.asList()了。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值