公式ドキュメントで Groovy (2.4.4) 構文を調べているときに、識別子として GString を使用したマップに関する特別な動作に遭遇しました。ドキュメントで説明されているように、評価されていない GString オブジェクトのハッシュコードは、評価された GString と同じ表現を持つ通常の String オブジェクトとは異なるため、GString は (ハッシュ) マップ識別子としては悪い考えです。
例:
def key = "id"
def m = ["${key}": "value for ${key}"]
println "id".hashCode() // prints "3355"
println "${key}".hashCode() // prints "3392", different hashcode
assert m["id"] == null // evaluates true
ただし、私の直感的な期待は、実際の GString 識別子を使用してマップ内のキーをアドレス指定すると、実際には値が配信されるというものでしたが、そうではありませんでした。
def key = "id"
def m = ["${key}": "value for ${key}"]
assert m["${key}"] == null // evaluates also true, not expected
それが気になりました。そのため、この問題に関していくつかの提案があり、いくつかの実験を行いました。
(私は Groovy を初めて使用するので、その場でブレインストーミングを行っただけであることを覚えておいてください。問題の原因を調べようとした方法を読みたくない場合は、提案 #4 に進んでください)
提案#1。GString オブジェクトのハッシュコードは、何らかの理由で動作/実装されており、コンテキストまたは実際のオブジェクトに応じて異なる結果を提供します。
それはナンセンスであることがすぐに判明しました。
println "${key}".hashCode() // prints "3392"
// do sth else
println "${key}".hashCode() // still "3392"
提案#2。マップまたはマップ アイテムの実際のキーに、期待される表現またはハッシュコードがありません。
マップ内のアイテム、キー、およびそのハッシュコードを詳しく調べました。
println m // prints "[id:value for id]", as expected
m.each {
it -> println key.hashCode()
} // prints "3355" - hashcode of the String "id"
したがって、マップ内のキーのハッシュコードは GString ハッシュコードとは異なります。はっ!か否か。知っておくと便利ですが、マップ インデックス内の実際のハッシュコードをまだ知っているので、実際には関係ありません。インデックスに入れられた後に文字列に変換されたキーを再ハッシュしました。じゃあ他には?
提案#3。GString の equals-method には、不明または実装されていない動作があります。
2 つのハッシュコードが等しいかどうかに関係なく、マップ内の同じオブジェクトを表すとは限りません。これは、キー オブジェクトのクラスに対する equals メソッドの実装に依存します。たとえば、equals メソッドが実装されていない場合、ハッシュコードが同一であっても 2 つのオブジェクトは等しくないため、目的のマップ キーを適切にアドレス指定できません。だから私は試しました:
def a = "${key}"
def b = "${key}"
assert a.equals(b) // returns true (unfortunate but expected)
したがって、同じ GString の 2 つの表現はデフォルトで等しくなります。
試した他のいくつかのアイデアを飛ばして、この投稿を書く直前に試した最後のアイデアを続けます。
提案#4。アクセスの構文は重要です。
それは理解の真のキラーでした。私は前に知っていました: 2 つのマップ値にアクセスする方法は構文的に異なります。それぞれの方法には制限がありますが、結果は同じままだと思いました。さて、これが出てきました:
def key = "id"
def m = ["${key}": "value for ${key}"]
assert m["id"] == null // as before
assert m["${key}"] == null // as before
assert m.get("${key}") == null // assertion fails, value returned
したがって、マップの get メソッドを使用すると、最初に期待した方法で実際の値を取得できます。
GStrings に関するこのマップ アクセス動作の説明は何ですか? (または、ここにどのような新人ミスが隠されていますか?)
お待ち頂きまして、ありがとうございます。
編集:私の実際の質問が明確に述べられていないことを恐れているので、ここに簡潔で簡潔なケースがあります:
このようなキーとして GString を持つマップがある場合
def m = ["${key}": "value for ${key}"]
なぜこれは値を返すのですか
println m.get("${key}")
しかし、そうではありません
println m["${key}"]
?