Groovy 1.7.1から、MapにメソッドwithDefaultが追加されています。
通常、マップにキーが存在していない場合のデフォルト値はJavaの時代からnullと決まってますが、withDefaultでnullではないデフォルト値を設定することができます。
何が嬉しいかというと、たとえば、ワードカウント処理をするとき、今まで
def map = [:] それぞれのワードに対してeach { map[it] = map[it]==null ? 1 : map[it] + 1 }
と書かなければならなかったところを、デフォルト値を0と設定することで
def map = [:].withDefault{0} それぞれのワードに対してeach { map[it]++ }
とこう書けます。便利。
なお、デフォルト値はクロージャで設定するので、例えばキーに応じて動的にデフォルト値を決めることができます。
withDefaultのデフォルト値設定は、以下のように動作します。
- withDefault{}で、delegateパターンを用いて、get時にある処理を介在させた新たなマップを作る。
- 「get時のある処理」とは、「もしキーが存在しなかったらwithDefaultに指定されたクロージャを呼び出し、その返り値をもとのマップの指定したキーの値として突っ込む(そしてその値が返る)」というもの*1。つまり存在しないキーに対するget時に、デフォルト値のput動作を実行します。
結果的に、「メモ化」のようなことをしてくれます。メモ化と言えば、フィボナッチ数の計算の効率化が定番なので、このwithDefaultが提供するメモ化機能を用いてやってみましょう。
fib = [:].withDefault{ k-> if (k==0) { 0 } else if (k==1) { 1 } else { fib[k-1]+fib[k-2] } } assert fib[16] == 987 (1..20).each { println fib[it] } // 1,2,2,3,5,8 ...
便利ですね。
*1:クロージャ中で、対象mapに処理対象のキーに対する値を自分で保存しても、上書きされるので注意。