きどたかのブログ

いつか誰かがこのブログからトラブルを解決しますように。

数字のキャッシュ

以前の日記をもう一度書いているようなもんなんだが…。


先に付加情報を書いておく。
-XX:AutoBoxCacheMax=というVMオプションが効くようだ。
たぶんこれもJava6.0 update 14からなんだろうが、
私が確認した環境はupdate 17です。


これを付けて、オートボクシングを試したわけじゃない。
代わりにInteger#valueOf(int)を試していた。
127と128を試している。
通常のキャッシュ範囲は-128〜127なのだが、
このオプションで-XX:AutoBoxCacheMax=128と渡してみたら、
128もちゃんとキャッシュされており、==演算子でtrueが返ってきた。
まあ、オートボクシング=valueOfへの置き換えだからな。


これはシステムプロパティに、
"java.lang.Integer.IntegerCache.high"というキーで設定しなくても、
VMオプションでも同じ設定が出来るということのようです。


気になったのでついでに実験してみたことがあります。
「-XX:AutoBoxCacheMaxはInteger以外にも効く設定か??」
というわけでLongで試したんですが、残念なことに効きませんでした。


私の過去の日記からキャッシュの内容についておさらいしときます。
Byte -128〜127
Character 0〜127
Short -128〜127
Integer -128〜127 上限は上述の方法で変更可能。
Long -128〜127
Float なし
Double なし
BigInteger -16〜16
BigDecimal 「スケール0」の0〜10と、0の「スケール0〜スケール15」



キャッシュは1つの配列に持つ場合と、正負で計2つ配列を持つ場合と、BigDecimalのようにスケール軸で持つことがある。
これらの実装から窺われるベストプラクティスがあります。
配列でアクセスせよ!


一般的には①1つの配列か、②正負で計2つ配列を持つことになります。
①の実装の場合、負のキャッシュ数は固定にします。
多くの場合、正も負も固定ですが、大事なのは負のキャッシュ数です。
Integerは上限を変えられますが、負のキャッシュ数は変えられません。
これは配列インデックスを導くのを楽にするためです。


②はBigIntegerのパターンです。
厳密には正とゼロと負で構成されています。
①の時にも言えることですが、valueOfメソッド内で使うのは単純な演算子のみです。
このパターンを使う場合に大事なことは、
正も負も同じキャッシュ数であるという制限です。
いや、実際には正と負の上限を異なるものにする実装も可能ですが、
「絶対値」のint配列インスタンスを有効に使いたいために同じにしているように見えます。


BigDecimalのパターン。
これは他と違ってスケールという情報があるため、
スケール0制限の中のキャッシュという面と、
同一の値でスケールのバリエーションという形を取っています。
残念なことにマイナスはキャッシュされていません。
日割り計算をする時、ひょっとするとBigDecimalを使うのでしょうが、
31という数字はキャッシュされていないので度々生成しています。
これがBigDecimalのダメなところです。


①のIntegerのパターンと、②のBigIntegerのパターンを混ぜると、
BigIntegerは正と負で違ったキャッシュ数を持てますし、
BigDecimalも同様に正と負で違ったキャッシュ数を持てるでしょう。


java.langパッケージのキャッシュと、
java.mathパッケージのキャッシュはあまりにも実装が違います。
きっと作者が違うのでしょう。
どちらがエレガントかと言うと、java.langパッケージに思えます。
静的ネストクラスにCacheという責務を閉じ込めています。


何かのキャッシュを自身で構築する際に、Mapを使ったりすることもあるでしょうが、
もしもプリミティブラッパーがキーになっているのであれば、キャッシュが効くかどうかは考えるべきです。