使用withDefault创建地图,放置元素时导致null

Coo*_*per 5 groovy

我试图使用Groovy方式创建TreeMap<String, List<Data>>具有默认值的a ,以便在键不存在的情况下轻松地将数据添加到新列表中。

TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>) [:].withDefault { [] }

如您所见,我要求使用a TreeMap并且withDefault仅返回一个Map实例,因此我需要进行强制转换。

当我尝试向地图添加新列表时,

myData[newKey].add(newData)

myData[newKey]null。但是,如果我更改Map初始化以删除强制TreeMap类型转换(并将类型更改为just Map而不是TreeMap),则myData[newKey].add(newData)可以按预期工作。

这是什么原因呢?withDefault投射地图后可以使用吗?

Jef*_*own 3

问题不仅仅在于演员阵容。它还与声明的类型有关。问题可以简化为这样:

def map1 = [:].withDefault { 0 }
TreeMap map2 = map1
Run Code Online (Sandbox Code Playgroud)

执行时map1是 的实例groovy.lang.MapWithDefault并且map2是 的实例java.util.TreeMap。它们是堆上的 2 个独立对象,而不仅仅是指向同一对象的 2 个引用。 map2不会有任何与之相关的默认行为。就好像你已经这样做了:

def map1 = [:].withDefault { 0 }
TreeMap map2 = new TreeMap(map1)
Run Code Online (Sandbox Code Playgroud)

这就是您的代码所发生的情况。强制转换和泛型只会让您的代码变得不太清晰。

这:

TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>) [:].withDefault { [] }
Run Code Online (Sandbox Code Playgroud)

可以细分为:

def tmpMap = [:].withDefault { [] }
TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>)tmpMap
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助。

编辑:

看到同样的事情发生的另一种方法是执行以下操作:

Set names = new HashSet()
ArrayList namesList = names
Run Code Online (Sandbox Code Playgroud)

当第二行执行时,会创建一个新的 ArrayList,就像您已经完成一样ArrayList namesList = new ArrayList(names)。这看起来与代码中的不同,但正在发生同样的事情。您有一个与其关联的静态类型的引用,并将该引用指向不同类型的对象,并且 Groovy 正在创建您声明的类型的实例。在上面的这个简单示例中,声明的类型是ArrayList。在您的示例中,声明的类型是TreeMap<String, List<Data>>.