查表法(Table-Driven-Method)主要是藉由表格的維度和對應的數值來替代冗長的判斷式(if-else or switch)
查表法根據不同的查表方式,可分為三種:
直接存取(direct access):可從表格中直接讀取數值。
索引存取(indexed access):先透過索引取出key值,再從key值取出數值。
階梯存取(stair-step access):對於不同的數值範圍有效。
 


 
直接存取:
如果要查詢月份對應的天數,最直接的寫法 e.g.,

    private int getDaysOfMonth(int month)
    {
        if (month == 1) {
            return 31;
        } else if (month == 2) {
            return 28;
        } else if (month == 3) {
            return 31;
        } else if (month == 4) {
            return 30;
        } else if (month == 5) {
            return 31;
        } else if (month == 6) {
            return 30;
        } else if (month == 7) {
            return 31;
        } else if (month == 8) {
            return 31;
        } else if (month == 9) {
            return 30;
        } else if (month == 10) {
            return 31;
        } else if (month == 11) {
            return 30;
        } else if (month == 12) {
            return 31;
        } else {
            throw new IllegalArgumentException();
        }
    }

可以藉由以下表格來取代

private static final int[] DAYS_OF_MONTH = {
            31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

取值如下,第7行為使用查表法的動作。

    @Test
    public void testGetDayOfMonth()
    {
        int[] months = {1,2,3,4,5,6,7,8,9,10,11,12};
        for(int month : months){
            int resultBySwitch = getDaysOfMonth(month);
            int resultByTable = DAYS_OF_MONTH[month - 1];
            assertEquals(resultBySwitch, resultByTable);
        }
    }

 


 
索引存取:
如果條件判斷式的變數型態不是int , 就無法使用陣列來建表,必須改用Map,而 Map 的 key 值即是索引值。e.g.

    private int getDayOfMonthByName(String nameOfMonth)
    {
        if (nameOfMonth.equals("Jan")) {
            return 31;
        } else if (nameOfMonth.equals("Feb")) {
            return 28;
        } else if (nameOfMonth.equals("Mar")) {
            return 31;
        } else if (nameOfMonth.equals("Apr")) {
            return 30;
        } else if (nameOfMonth.equals("May")) {
            return 31;
        } else if (nameOfMonth.equals("Jun")) {
            return 30;
        } else if (nameOfMonth.equals("Jul")) {
            return 31;
        } else if (nameOfMonth.equals("Aug")) {
            return 31;
        } else if (nameOfMonth.equals("Sep")) {
            return 30;
        } else if (nameOfMonth.equals("Oct")) {
            return 31;
        } else if (nameOfMonth.equals("Nov")) {
            return 30;
        } else if (nameOfMonth.equals("Dec")) {
            return 31;
        } else {
            throw new IllegalArgumentException();
        }
    }

使用Map取值,第1到10行為初始化Map , 第14行為使用Map取值。

        Map<String, Integer> DAY_OF_MONTH = new HashMap<String,Integer>();
        String[] nameOfMonth = new String[] {
                "Jan", "Feb", "Mar", "Apr", "May","Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
        };
        Integer[] numberDayOfMonth = {
                31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
        };
        for (int i = 0; i < nameOfMonth.length; ++i) {
            DAY_OF_MONTH.put(nameOfMonth[i], numberDayOfMonth[i]);
        }
        for(String name : nameOfMonth){
            int resultByMethod = getDayOfMonthByName(name);
            int resultByMap = DAY_OF_MONTH.get(name);
            assertEquals(resultByMethod, resultByMap);
        }

 


 
階梯存取:
假設現在要對不同分數給予等級的判斷, e.g.

    private String getNameOfGradeByMethod(int score)
    {
        if (score >= 90) {
            return "A";
        } else if (score >= 80 && score < 90) {
            return "B";
        } else if (score >= 70 && score < 80) {
            return "C";
        } else if (score >= 60 && score < 70) {
            return "D";
        } else if (score < 60) {
            return "F";
        } else {
            throw new IllegalArgumentException();
        }
    }

需要建立2個對應的table , 以及求值的函式(getNameOfGradeByTable)。 e.g.

    private static final String NAME_OF_GRADE_LEVEL[] = {
            "A", "B", "C", "D", "F"
    };
    private static final int NUMBER_OF_GRADE_LEVEL[] = {
            90, 80, 70, 60
    };
    private String getNameOfGradeByTable(int score)
    {
        int gradeLevel = 0;
        while (NAME_OF_GRADE_LEVEL[gradeLevel] != NAME_OF_GRADE_LEVEL[NAME_OF_GRADE_LEVEL.length - 1]) {
            if (score < NUMBER_OF_GRADE_LEVEL[gradeLevel]) {
                ++gradeLevel;
            } else {
                break;
            }
        }
        return NAME_OF_GRADE_LEVEL[gradeLevel];
    }

雖然整體看起來比判斷式來的長,但若是接下來需要增加更多的判斷如 60, 50, 40, 30, 20, 10。
對於查表法只要在 table 加入相對應數值即可。
也適合用於沒有規則變化的求值。


 
事實上查表法無法去除掉原判斷式的邏輯,它只是把邏輯搬移到表中。表格的複雜度會跟著原判斷式的邏輯成正比。
另外也需要提供空間儲存表格。
查表法可以減少程式碼的長度,但無法簡化程式碼。
對可讀性的幫助並不大,反而需要完全理解表格才能修改原功能或是增加新功能。
因此最適合的情況為冗長但判斷邏輯簡單的條件式。
更複雜的查表法範例 ref