HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

"値が入力されている"の判定

“値が入力されている”ということを調べるのに、

if (s != null && !s.equals("")) {
      ‥
}

とか、

if (s != null && s.length() > 0) {
      ‥
}

という判定条件を書くことはよくあると思う。

if (!"".equals(s)) {
      ‥
}

というのもあるかな。
ここで“値が入力されている”というとき、“その値とは1文字以上の文字列である”という暗黙の了解があるという前提の下で上記のようなコードが生まれてくる。
仕事で人が書いたコードを見るときによく見かけるのは一番最初のケース。三番目のケースも時々見かける。一方、私自身は、特にどういうポリシーがあって、ということではないが二番目のケースを使用する。
また例によって(謎)計ってみるが、その結果は抜きにして、なぜ二番目のケースを使うのかを考えてみた。
一番目は「なんとなく違うな」と感じる。知りたいのは“1文字以上の文字列であるかどうか”なので、特定の文字列との比較を持ち出すのは「違う」かな、と。
三番目は論外。一時期、nullチェックをしなくていいということから自分の中で流行ったことがあるが、ここでの条件判定の主体は変数sなので、三番目のように書いてあると思考が一瞬停止している自分に気づくことがある。“慣れ”だと言われたらそうかもしれないけど‥
で、計ってみる。今回はよほどの回数ループを回さないと差が出ないだろうな、という読みで‥

import java.text.DecimalFormat;
import java.text.NumberFormat;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class TestValueCheck {

    private static NumberFormat nf = new DecimalFormat("0.000");

    private static String s = "abc";
    private static int loopCount = 10000;

    private long start;
    private long end;
    private String name;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    @Before
    public void setUp() throws Exception {
        start = System.currentTimeMillis();
    }

    @After
    public void tearDown() throws Exception {
        end = System.currentTimeMillis();
        System.out.printf("%-16s %7s %24s\n", name, loopCount, nf.format((end - start) / 1000.0) + " sec");
    }

    @Test
    public void testLength() {
        name = "length";
        for (int i = 0; i < loopCount; ++i) {
            for (int j = 0; j < loopCount; ++j) {
                if (s != null && s.length() > 0) {
                    
                }
            }
        }
    }
    @Test
    public void testEquals() {
        name = "equals";
        for (int i = 0; i < loopCount; ++i) {
            for (int j = 0; j < loopCount; ++j) {
                if (s != null && !s.equals("")) {
                    
                }
            }
        }
    }
    @Test
    public void testEquals2() {
        name = "equals(2)";
        for (int i = 0; i < loopCount; ++i) {
            for (int j = 0; j < loopCount; ++j) {
                if (!"".equals(s)) {
                    
                }
            }
        }
    }
}

「指定したループ回数の二乗」回だけ条件判定を行うのにかかる時間を測定。
で、結果。

length             10000                0.607 sec
equals             10000                2.113 sec
equals(2)          10000                1.962 sec

String#length()を使ったほうが3倍早いという結果に。まぁ、1億回もループして1.3〜1.5秒の差なので‥‥