« 2010年09月 | メイン | 2010年11月 »

2010年10月 アーカイブ

2010年10月02日

AutoPagerizeのページ数を常に表示させるGreaseMonkey

AutoPagerizeを使って、ページ数の多いサイトを読んでいると、何ページを見ているのかわからなくなる事がある。
各ページの先頭に、AutoPagerizeがヘッダを挿入してくれるが、各ページの文量が多いとヘッダが見えない。
そこで、今見ているページのAutoPagerizeのヘッダを常に表示するUserScriptを作った。
まあ、正確には1ページ目では表示されないんだけど...。
こんな風に表示される。
ページが切り換わる時の動き、横スクロールの時の動きも工夫したら、予想以上にぬるぬる動いて心地が良い。
印刷の時にじゃまになるので、赤い「x」をクリックすると消えるようにしている。

2010年10月03日

WebSocketRemoteのデモ映像を作った

ずっと作成途中のままだったWebSocketRemoteのデモのスクリーンキャストを仕上げた。
映像の中で「1fps」としているけど、実際はWebSocketRemoteが動いているサーバ側では次のような処理をしているから。
sleep(Math.max(Math.min((endTime-startTime)*2,1000),100));
startTimeとendTimeは、「画面キャプチャ=>画像変換」の処理時間。
ここを次のように書き換えると3~4fpsは出る。
sleep(100);
ただ、こうすると、手元の環境ではCPUの占有率が90%近くなってしまう。
一応、ハイスペックなマシン用をGitHubで公開している。
このハイスペック用の処理は以下の通り。
sleep(Math.max(Math.min((endTime-startTime)*2,100),30));
本当は、sleepの引数、port番号、転送する画像形式を変更できるようにしたいんだけど...。

2010年10月28日

HashMap.keySetは毎回新しいインスタンスを作成しているわけではない

複数の人に勧められて、Effective Java 第2版を読んでいたら予想外のことが書いてあった。
たとえば、MapインターフェースのkeySetメソッドは、そのマップ内のすべてのキーから構成されているMapオブジェクトのSetビューを返します。一見、keySetの呼び出しは、その都度、新たなSetインスタンスを生成しなければならないように見えますが、あるMapオブジェクトに対するkeySetの呼び出しすべてが、同じSetインスタンスを返しても構いません。返されたSetインスタンスはたいていは可変ですが、返されたオブジェクトはすべて機能的に同じです。つまり、1つの返されたオブジェクトが変更されたら、他の返されたオブジェクトすべても変更されます。なぜならば、それらのオブジェクトはすべて、同じMapインスタンスによりバックアップされているからです。keySetビューオブジェクトの複数インスタンスを生成しても無害ですが、複数生成する必要もありません。
今まで、keySetメソッドで取得したSetオブジェクトは、下記のような動作をすると思っていた。
HashMap hash=new HashMap();
hash.put("a", "aaa");
Set key1 = hash.keySet();
System.out.println(key1);              //=>[a]
hash.put("b", "bbb");
System.out.println(key1);              //=>[a]
key1.add("z");
System.out.println(key1);              //=>[a, z]
key1.remove("a");
System.out.println(key1);              //=>[z]
Set key2 = hash.keySet();
System.out.println(key2);              //=>[a, b]
System.out.println(key1==key2);        //=>false
System.out.println(hash);              //=>{a=aaa, b=bbb}
つまり、keySetメソッドで取得したSetオブジェクトは、それぞれ別のインスタンスであると考えていた。
しかし、実際は次のような動作をした。Javaのバージョンは1.6。
HashMap hash=new HashMap();
hash.put("a", "aaa");
Set key1 = hash.keySet();
System.out.println(key1);              //=>[a]
hash.put("b", "bbb");
System.out.println(key1);              //=>[b, a]
//key1.add("z");                       //=>error!
System.out.println(key1);              //=>[b, a]
key1.remove("a");
System.out.println(key1);              //=>[b]
Set key2 = hash.keySet();
System.out.println(key2);              //=>[b]
System.out.println(key1==key2);        //=>true
System.out.println(hash);              //=>{b=bbb}
HashMapオブジェクトhashのキーを変更すると、先に取得してたSetオブジェクトkey1の要素も変更されている。
別々のタイミングで取得した別々のSetオブジェクト1ey1とkey2が等値となっており、key2取得前にkey1を変更していても等値なのは変わらない。
中でも驚いたのが、Setオブジェクトkey1から要素を削除すると、HashMapの要素も削除されること。
HashMap.keySetをJavaDocを確認すると次のように書かれていた。
このマップに含まれるキーの Set ビューを返します。セットはマップと連動しているので、マップに対する変更はセットに反映され、また、セットに対する変更はマップに反映されます。セットの反復処理中にマップが変更された場合、反復処理の結果は定義されません (反復子自身の remove オペレーションを除く)。セットは要素の削除をサポートしており、対応するマッピングをマップから削除できます。削除は、Iterator.remove、Set.remove、removeAll、retainAll、および retainAll オペレーションを通して行います。Set は、add オペレーションや addAll オペレーションはサポートしていません。 
つまり、上記のような動作を望まないのであれば、keySetメソッドの戻り値を元に、新たにインスタンスを作成すればいい。
HashMap hash=new HashMap();
hash.put("a", "aaa");
Set key1 = new HashSet(hash.keySet());
System.out.println(key1);              //=>[a]
hash.put("b", "bbb");
System.out.println(key1);              //=>[a]
key1.add("z");
System.out.println(key1);              //=>[a, z]
key1.remove("a");
System.out.println(key1);              //=>[z]
Set key2 = new HashSet(hash.keySet());
System.out.println(key2);              //=>[a, b]
System.out.println(key1==key2);        //=>false
System.out.println(hash);              //=>{a=aaa, b=bbb}
Google

タグ クラウド