Groovy中Map的奇怪行为

5

我正在编写代码,发现在处理 XML 和 Maps 时,Groovy 的行为有些奇怪。我思考了一下,但是无法弄清为什么会发生这种情况,也不确定是否应该这样。

我编写了三个示例代码。map1 和 map3 之间的关键区别仅在以下部分:

Map1:

map1 << ["${it.name()}":it.value()]

Map3:

map3["${it.name()}"]=it.value()

这是完整的代码,您可以将其复制粘贴到Groovy控制台中:

def xml = '<xml><head>headHere</head><body>bodyHere</body></xml>'


Map map1 = [:]

def node = new XmlParser().parseText(xml) 

node.each {
      map1 << ["${it.name()}": it.value()]
} 

println map1
println map1["head"]

println ">>>>>>>>>>>>>>>>>>>>>>"



Map map2 = [:]

map2 << ["head":"headHere"]
map2 << ["body":"bodyHere"]

println map2
println map2["head"]

println "<<<<<<<<<<<<<<<<<<<<<<"



def xml2 = '<xml><head>headHere</head><body>bodyHere</body></xml>'    

Map map3 = [:]

def node2 = new XmlParser().parseText(xml2) 

node2.each {
      map3["${it.name()}"]=it.value()
} 

println map3
println map3["head"]

我得到的结果如下:

我得到的结果如下:

[head:[headHere], body:[bodyHere]]
null

[head:headHere, body:bodyHere]
headHere

[head:[headHere], body:[bodyHere]]
[headHere]

尽管map1和map3看起来相同,但map["head"]的结果完全不同,第一个返回null,而第二个返回实际结果。我不明白为什么会这样。我花了一些时间研究它,但还是不理解。我使用.getProperty()获取类的信息,但在map和对象内部看起来都是相同的。我尝试了其他几件事情,但没有任何思路。我甚至尝试了不同的操作系统(Win XP、Mac OS),但还是没有任何进展。
我已经没有任何想法了,请问有人能解释这种奇怪的行为吗?为什么会发生这种情况?map << [key:object]和map[key] = object之间有什么区别?
谢谢。

1
Groovy官方文档中的Strings and GStrings页面有一节警告我们GStrings不是Strings,大约在页面2/3处。 - tim_yates
2个回答

12

有一件可能会有所帮助的事情是,不要使用GStrings作为您的键。 Groovy支持通过将对象包装在括号中直接使用它们作为键。

来自手册

默认情况下,Map键是字符串:[a:1]等同于["a":1]。但是,如果您真的希望变量成为键,您必须将其包装在括号中:[(a):1]。

完全可工作的示例代码:

def xml = '<xml><head>headHere</head><body>bodyHere</body></xml>'

Map map1 = [:]
def node = new XmlParser().parseText(xml)
node.each {
    map1 << [ (it.name()): it.value() ]
}

println map1
println map1["head"]
println ">>>>>>>>>>>>>>>>>>>>>>"

Map map2 = [:]

map2 << ["head":"headHere"]
map2 << ["body":"bodyHere"]

println map2
println map2["head"]

println "<<<<<<<<<<<<<<<<<<<<<<"

def xml2 = '<xml><head>headHere</head><body>bodyHere</body></xml>'

Map map3 = [:]

def node2 = new XmlParser().parseText(xml2)

node2.each {
    map3[it.name()] = it.value()
}

println map3
println map3["head"]

输出结果为:

[head:[headHere], body:[bodyHere]]
[headHere]
>>>>>>>>>>>>>>>>>>>>>>
[head:headHere, body:bodyHere]
headHere
<<<<<<<<<<<<<<<<<<<<<<
[head:[headHere], body:[bodyHere]]
[headHere]

2
这是一个演示Groovy中双引号字符串的特性的例子quirk of double quoted strings in Groovy
双引号字符串如果没有插值表达式,则为普通的java.lang.String,但如果存在插值,则为groovy.lang.GString实例。
groovy:000> m = [:]
===> {}
groovy:000> tmp = "wat"
===> wat
groovy:000> key = "${tmp}"
===> wat
groovy:000> m << ["${key}": "hi"]
===> {wat=hi}
groovy:000> m["${key}"] = "hi"
===> hi
groovy:000> m
===> {wat=hi, wat=hi}
groovy:000> m["wat"] = "fuuuuuu!"
===> fuuuuuu!
groovy:000> m
===> {wat=hi, wat=fuuuuuu!}
groovy:000> m.keySet().each { println it.class }
class org.codehaus.groovy.runtime.GStringImpl
class java.lang.String

享受吧 ;)

查看键的类。 - Dave Newton
我明白我在看不同的对象,所以才会在我的代码中得到null的原因。但我不明白的是为什么有时候是GString,有时候又是String。而且,如果键的值是GString或其他任何类型也没关系。但键看起来有点奇怪...我的意思是键可以是任何类的实例,但如果我期望所有的键都是字符串,而实际上却得到了GString,那么就可能导致一些错误和问题。 - MeIr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接