0

假设我有以下Set

Set<String> fruits = new HashSet<String>();
fruits.add("Apple")
fruits.add("Grapes")
fruits.add("Orange")

如果我想创建一个防御性副本,这样如果原始列表被修改,副本不会反映它,我可以这样做:

Set<String> unmodifiableFruits = Collections.unmodifiableSet(new HashSet(fruits))

所以如果我这样做:

fruits.add("Pineapple")     
println(unmodifiableFruits)

unmodifiableFruits不会有菠萝。

或者我可以这样:

Set<String> unmodifiableFruits = Collections.unmodifiableCollection(fruits)

结果是一样的,unmodifiableFruits不会有菠萝。

问题:

  1. 假设如果我fruits作为参数传递给一个类,首选方法是Collections.unmodifiableCollection()

原因是,我读过在构造函数中声明new是一种不好的做法,如果我要使用Collections.unmodifiableSet(),我需要声明一个new HashSet<String>(fruits).

  1. 为什么我不能这样做?

    Collections.unmodifiableSet(fruits)

并让它返回一个不可修改的集合。

相反,我必须这样做:

Collections.unmodifiableSet(new HashSet<String>(fruits))

是不是因为 Set 是一个接口,它不知道要返回哪个实现?

4

1 回答 1

2

Groovy 增强了集合方法,这意味着它向标准集合类添加了方法。

这些方法之一是toSet()

将集合转换为集合。即使集合已经是一个集合,也总是返回一个新集合。

示例用法:

def result = [1, 2, 2, 2, 3].toSet()
assert result instanceof Set
assert result == [1, 2, 3] as Set

当你写这个:

Set<String> unmodifiableFruits = Collections.unmodifiableCollection(fruits)

它意味着调用将返回的数据.toSet()强制转换为 a ,隐式复制数据CollectionunmodifiableCollectionSet

当你写这个:

Set<String> unmodifiableFruits = Collections.unmodifiableSet(fruits)

返回的值已经是 a Set,所以toSet()不被调用,意思是unmodifiableFruitsfruits共享数据。

这就是为什么你必须在使用时显式复制数据的原因unmodifiableSet,添加new HashSet(...).

Collections.unmodifiableCollection()将集合传递给构造函数时是否使用正确的方法?

绝对不。使用unmodifiableCollection()并分配给 aSet,隐式调用toSetwhich 复制数据,隐藏了执行副本的事实。

为了确保代码的可读性,即任何阅读代码的人(包括 3 年后的您自己)都会理解它的作用,请编写代码以使用复制构造函数显式复制数据。

好吧,当然,除非这是一个代码混淆练习,在这种情况下,这是一个很好的误导技巧。

于 2018-08-29T02:34:00.683 回答