メモ、音楽

きれいなギターサウンドを聴きたいな。
と思って探していて、ドツボにはまったのがこれ。



動画右上を見ると、おそらく韓国の女性の方が弾いているのかな?
韓流ブームなんていうけれど、アイドルよりこういうかっちょいい音を作る人をテレビで紹介してほしいものです。

--------------------------------------------------------------------------------------

メモ・・・ループ内でStringを+で連結するとFindBugsで警告される話

javaでループ処理内で文字列を連結させていく場面が出てくるときがあります。
そのとき、単純に+で文字列連結するとFindBugsで警告がでるときがあります。
 
ちょっと簡単に以下のようなソースを入れると警告がでます。

        String str = "";
        for (int i = 0; i < 10000; i++) {
            str += Integer.toString(i);
        }


---------------------------------------------------------
Pattern: このメソッド内で、+を使ってループ内で文字列を連結しています。
id: SBSC_USE_STRINGBUFFER_CONCATENATION, type: SBSC, category: PERFORMANCE

このメソッドの中で、ループ内でStringの連結を繰り返しているようです。このためStringBuffer/StringBuilderへの変換、連結、そしてStringへの再変換という処理が何度も繰り返されます。文字列が毎回コピーし直されて長くなっていくため、繰り返し回数の二乗の処理コストが必要となる場合があります。

明示的にStringBufferあるいはStringBuilder(J2SE 1.5から導入されます)を用いる事で、パフォーマンスを改善する事が出来ます。

例)


// 悪い例

String s = "";

for (int i = 0; i < field.length; ++i) {

s = s + field[i];

}



// 良い例

StringBuffer buf = new StringBuffer();

for (int i = 0; i < field.length; ++i) {

buf.append(field[i]);

}

String s = buf.toString();

---------------------------------------------------------

これはSJC-Pの取得で勉強したときにも、参考書に記述されていたのですが、
そんなに実行速度が変わるのか疑問でした。
というわけで、ちょっと測ってみたいと思います。


実測方法はjava.lang.ObjectのnanoTime()メソッドの説明に記述されている方法を使用して、
5回実行してその平均を求めてみます。



まずは普通に文字列連結した場合。

package main;

import bean.HogeBean;

public class TestHoge {

    public static void main(String[] args) {

        long startTime = System.nanoTime();

        String str = "";
        for (int i = 0; i < 10000; i++) {
            str += Integer.toString(i);
        }

        long estimatedTime = System.nanoTime() - startTime;
        System.out.println("estimatedTime = " + estimatedTime);

    }

}


これを5回実行してみると
---------------------------------------------------------
estimatedTime = 1330430863
estimatedTime = 1325961511
estimatedTime = 1326294489
estimatedTime = 1391642798
estimatedTime = 1333323965
---------------------------------------------------------
となりまして、平均1341530725.2(ナノ秒)。
ナノ秒ってわかりづらいですね。
単位を変えると、
1341530.7(マイクロ秒)
1341.5(ミリ秒)
ってことですね。

さて、次にFindbugsの例にあるようにStringBufferを使用して以下のようなソースを実測してみます。

package main;

import bean.HogeBean;

public class TestHoge {

    public static void main(String[] args) {

        long startTime = System.nanoTime();

        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 10000; i++) {
            sb.append(Integer.toString(i));
        }

        long estimatedTime = System.nanoTime() - startTime;
        System.out.println("estimatedTime = " + estimatedTime);

    }

}


これを5回実行してみると
---------------------------------------------------------
estimatedTime = 9043554
estimatedTime = 8992905
estimatedTime = 9728598
estimatedTime = 8811576
estimatedTime = 9466551
---------------------------------------------------------
となりまして、平均9208636.8(ナノ秒)
9208.6(マイクロ秒)
9.2(ミリ秒)

当方のパソコンの環境では10000回の単純な繰り返しで1秒も速度が変わることがわかりました。
これはかなり実行速度が変わったといっていいでしょうね。

なお、StringBufferとなっているところをStirngBuilderで連結するほうがさらに早くなるそうで、実測してみると
---------------------------------------------------------
estimatedTime = 7911682
estimatedTime = 8961621
estimatedTime = 8341625
estimatedTime = 8887203
estimatedTime = 8400948
---------------------------------------------------------
平均8500615.8(ナノ秒)
8500.6(マイクロ秒)
8.5(ミリ秒)

となりました。
StringBufferとStirngBuilderの違いはAPI参照してください。


追記)
比較するプログラム内容がちょっとおかしいような気がしますが・・・修正が面倒なのでこのままにしておきます。

--------------------------------------------------------------------------------------

メモ・・・javaでBeanを作成する際、何も考えずgetterを作るとFindBugsで警告される話

前回はsetterについてお話を書いたので、今回はgetterに関して。

まずはBeanクラスについて。
これは前回setterについて書いたものを使用してみます。

package bean;

import java.util.Arrays;

public class HogeBean {

    private String[] hogeStrAr;

    public String[] getHogeStrAr() {
        return hogeStrAr;
    }

    public void setHogeStrAr(String[] hogeStrAr) {
        if(hogeStrAr == null){
            this.hogeStrAr = null;
        }else{
            this.hogeStrAr = (String[])hogeStrAr.clone();
        }
    }

    @Override
    public String toString() {
        return "HogeBean [hogeStrAr=" + Arrays.toString(hogeStrAr) + "]";
    }

}


この状態でFindBugsを使用すると、getterに以下のような警告メッセージが表示されます。

----------------------------------------------------------
Bug:
bean.HogeBean.getHogeStrAr() は HogeBean.hogeStrAr を戻すことにより内部表現を暴露してしまう可能性があります。
Pattern id: EI_EXPOSE_REP, type: EI, category: MALICIOUS_CODE

オブジェクトのフィールドに格納された変更可能なオブジェクトの参照を返すと、オブジェクトの内部表現を暴露してしまいます。もしも、このオブジェクトが信頼されていないコードによってアクセスされる事が、セキュリティや、その他の重要な情報への脅威となる可能性があるため、このコードは修正すべきです。防御的コピーを行って、複製を返すのが、多くの場面で良いやり方です。
---------------------------------------------------------

getterのどこがよろしくないのか検証するため、Beanを使用するクラスを以下のように作成しました。

package main;

import bean.HogeBean;

public class TestHoge {

    public static void main(String[] args) {

        String[] strAr = { "りんご", "みかん", "バナナ" };

        HogeBean hogeBean = new HogeBean();
        hogeBean.setHogeStrAr(strAr);

        // 現在のBeanの状態を確認
        System.out.println(hogeBean);

        //Beanの配列を取得してstrAr2に。
        String[] strAr2 = hogeBean.getHogeStrAr();

        //strAr2の0番目を変更してみる。
        strAr2[0] = "いちご";

        //Beanの状態を確認
        System.out.println(hogeBean);
    }

}


実行してみると
---------------------------------------------------------
HogeBean [hogeStrAr=[りんご, みかん, バナナ]]
HogeBean [hogeStrAr=[いちご, みかん, バナナ]]
---------------------------------------------------------
となりまして、getterで取得したものを変更すると、Beanの内容も変わってしまいます。

ですので、防御的コピーで対処しろってことなんですね。
setterのときと同様、ただ.cloneをつけるだけでは、nullが設定されている場合にNullPointerExceptionが発生してしまいます。
そのことを考慮して、以下のようにgetterを修正してみます。

    public String[] getHogeStrAr() {
        if (hogeStrAr == null) {
            return null;
        }
        return (String[]) hogeStrAr.clone();


そうすると、先ほどのTestHogeクラスを実行してみますと
---------------------------------------------------------
HogeBean [hogeStrAr=[りんご, みかん, バナナ]]
HogeBean [hogeStrAr=[りんご, みかん, バナナ]]
---------------------------------------------------------
となりまして、無事修正できました。

--------------------------------------------------------------------------------------

メモ・・・javaでBeanを作成する際、何も考えずsetterを作るとFindBugsで警告される話

業務でjavaによる開発はeclipseを使ってます。

っで、beanクラスを作るとき以下のような手順を使っています。

package bean;

public class HogeBean {

    private String[] hogeStrAr;

}



とまでソースを打ち込んだ後、
右クリック→ソース→getterおよびsetterの生成を選びます。
すると以下のようなソースが出来上がります。

package bean;

public class HogeBean {

    private String[] hogeStrAr;

    public String[] getHogeStrAr() {
        return hogeStrAr;
    }

    public void setHogeStrAr(String[] hogeStrAr) {
        this.hogeStrAr = hogeStrAr;
    }

}



この状態で保存して一安心した状態で、FindBugsのソースチェックを行うと
getterには
----------------------------------------------------------
Bug:
bean.HogeBean.getHogeStrAr() は HogeBean.hogeStrAr を戻すことにより内部表現を暴露してしまう可能性があります。
Pattern id: EI_EXPOSE_REP, type: EI, category: MALICIOUS_CODE

オブジェクトのフィールドに格納された変更可能なオブジェクトの参照を返すと、オブジェクトの内部表現を暴露してしまいます。もしも、このオブジェクトが信頼されていないコードによってアクセスされる事が、セキュリティや、その他の重要な情報への脅威となる可能性があるため、このコードは修正すべきです。防御的コピーを行って、複製を返すのが、多くの場面で良いやり方です。
---------------------------------------------------------

setterには
---------------------------------------------------------
Bug: bean.HogeBean.setHogeStrAr(String[]) は HogeBean.hogeStrAr の中に外部の可変オブジェクトを格納することにより内部表現を暴露してしまう可能性があります。
Pattern id: EI_EXPOSE_REP2, type: EI2, category: MALICIOUS_CODE

このコードは、外部の変更可能オブジェクトを自らの実装に格納しています。もしもこのオブジェクトが信頼されていないコードによってアクセスされると、セキュリティや、その他の重要な情報への脅威となる可能性があるため、このコードは修正すべきです。防御的コピーを行って、複製を返すのが、多くの場面で良いやり方です。
---------------------------------------------------------

というメッセージが表示されてしまいます。
eclipseのgetter、setter機能をもうちょいしっかりしてほしいところです。


さて、この警告はどうやら参照渡ししたものをそのままbeanのフィールドに設定・返却しているのがよろしくないそうです。
ちょっとよろしくない理由を検証してみます。

この記事ではsetterに関して記述します。
getterはまた今度。

まずBeanにtoStringメソッドをオーバーライドを追加してBeanの内容をチェックできるように修正。


package bean;

import java.util.Arrays;

public class HogeBean {

    private String[] hogeStrAr;

    public String[] getHogeStrAr() {
        return hogeStrAr;
    }

    public void setHogeStrAr(String[] hogeStrAr) {
        this.hogeStrAr = hogeStrAr;
    }

    @Override
    public String toString() {
        return "HogeBean [hogeStrAr=" + Arrays.toString(hogeStrAr) + "]";
    }

}


そして、Beanを実際に使用するソースを作成します。

package main;

import bean.HogeBean;

public class TestHoge {

    public static void main(String[] args) {

        String[] strAr = { "りんご", "みかん", "バナナ" };

        HogeBean hogeBean = new HogeBean();
        hogeBean.setHogeStrAr(strAr);

        // 現在のBeanの状態を確認
        System.out.println(hogeBean);

        // 配列の0番目を変更してみる。
        strAr[0] = "いちご";
        // 配列操作後のBeanの状態を確認
        System.out.println(hogeBean);
    }

}


実行結果は以下のようになります。

--------------------------------------------------------------------------------------------------
HogeBean [hogeStrAr=[りんご, みかん, バナナ]]
HogeBean [hogeStrAr=[いちご, みかん, バナナ]]
--------------------------------------------------------------------------------------------------

setterを使わずにBeanの中身が変わってしまうことが問題のようです。

というわけで"setterはコピーを使って修正しろ"とのことなので、以下のように修正してみます。

    public void setHogeStrAr(String[] hogeStrAr) {
        this.hogeStrAr = (String[])hogeStrAr.clone();
    }

この状態で先ほどのTestHogeクラスを実行してみると

--------------------------------------------------------------------------------------------------
HogeBean [hogeStrAr=[りんご, みかん, バナナ]]
HogeBean [hogeStrAr=[りんご, みかん, バナナ]]
--------------------------------------------------------------------------------------------------

となりました。
めでたしめでたし・・・と思いきや、まだ罠があったりします。

意図的にBeanのhogeStrArにnullを設定しようとするとNull`PonterExceptionが発生してしまいます。

package main;

import bean.HogeBean;

public class TestHoge {

    public static void main(String[] args) {

        String[] strAr = null;

        HogeBean hogeBean = new HogeBean();
        hogeBean.setHogeStrAr(strAr);

        // 現在のBeanの状態を確認
        System.out.println(hogeBean);

    }

}



実行結果
--------------------------------------------------------------------------------------------------
Exception in thread "main" java.lang.NullPointerException
 at bean.HogeBean.setHogeStrAr(HogeBean.java:14)
 at main.TestHoge.main(TestHoge.java:13)
--------------------------------------------------------------------------------------------------


そんなわけで、この現象を回避したいためnullかどうかの分岐を入れてみます。

    public void setHogeStrAr(String[] hogeStrAr) {
        if(hogeStrAr == null){
            this.hogeStrAr = null;
        }else{
            this.hogeStrAr = (String[])hogeStrAr.clone();
        }
    }

すると実行結果は
--------------------------------------------------------------------------------------------------
HogeBean [hogeStrAr=null]
--------------------------------------------------------------------------------------------------

となりまして、解決です。
次回はgetter編

--------------------------------------------------------------------------------------

2011年08月10日の日記

2011年08月10日。


ダイソーで157円のすだれを2つと?型のネジを購入。
暑さ対策で、自分の部屋の窓の外に取り付けてみました。

2011_08_10_sudare.jpg

以前、竹シーツを使用していると書きましたが(2011年07月01日参照)、日中に窓を開けておくと、陽が入って竹シーツに熱が篭もるので、日除けとして定番のすだれをとりつけです。

クーラーがない部屋なので、これで少しは夏を涼しくすごせるようになるのかな?

--------------------------------------------------------------------------------------

このブログについて
  • 全記事一覧(時間順)
  • このブログについて
  • 私のプロフィール
  • 当ブログで扱っている動画について
  • 記事違いなコメントのお返事

  • カテゴリー
    twitter
    カレンダー
    07 | 2011/08 | 09
    - 1 2 3 4 5 6
    7 8 9 10 11 12 13
    14 15 16 17 18 19 20
    21 22 23 24 25 26 27
    28 29 30 31 - - -
    Amazon
    でたらめな当ブログにぴったりな商品を自動で表示するみたいです。



    月別アーカイブ
    プロフィール

    たづみ

    Author:たづみ
    ・1981年生まれの男
    ・もう少し詳細なプロフィールはこちらで

    最新コメント
    アクセスランキング
    [ジャンルランキング]
    日記
    1757位
    アクセスランキングを見る>>

    [サブジャンルランキング]
    会社員・OL
    357位
    アクセスランキングを見る>>