1. 程式人生 > >謎題26:在循環中

謎題26:在循環中

nal args 但是 什麽 code ava 支持 同時 相同

下面的程序計算了一個循環的叠代次數,並且在該循環終止時將這個計數值打印了出來。那麽,它打印的是什麽呢?


public class InTheLoop {

    public static final int END = Integer.MAX_VALUE;

    public static final int START = END - 100;

    public static void main(String[] args) {

        int count = 0;

        for (int i = START; i <= END; i++)

            count++;

        System.out.println(count);

    }

}

如果你沒有非常仔細地查看這個程序,你可能會認為它將打印100,因為END比START大100。如果你稍微仔細一點,你可能會發現該程序沒有使用典型的循環慣用法。大多數的循環會在循環索引小於終止值時持續運行,而這個循環則是在循環索引小於或等於終止值時持續運行。所以它會打印101,對嗎?

嗯,根本不對。如果你運行該程序,就會發現它壓根就什麽都沒有打印。更糟的是,它會持續運行直到你撤銷它為止。它從來都沒有機會去打印count,因為在打印它的語句之前插入的是一個無限循環。

問題在於這個循環會在循環索引(i)小於或等於Integer.MAX_VALUE時持續運行,但是所有的int變量都是小於或等於Integer.MAX_VALUE的。因為它被定義為所有int數值中的最大值。當i達到Integer.MAX_VALUE,並且再次被執行增量操作時,它就有繞回到了Integer.MIN_VALUE。

如果你需要的循環會叠代到int數值的邊界附近時,你最好是使用一個long變量作為循環索引。只需將循環索引的類型從int改變為long就可以解決該問題,從而使程序打印出我們所期望的101:


for (long i = START; i <= END; i++)

更一般地講,這裏的教訓就是int不能表示所有的整數。無論你在何時使用了一個整數類型,都要意識到其邊界條件。如果其數值下溢或是上溢了,會怎麽樣呢?所以通常最好是使用一個取之範圍更大的類型。(整數類型包括byte、char、short、int和long。)

不使用long類型的循環索引變量也可以解決該問題,但是它看起來並不那麽漂亮:


int i = START;

do {

    count++;

}while (i++ != END);

如果清晰性和簡潔性占據了極其重要的地位,那麽在這種情況下使用一個long類型的循環索引幾乎總是最佳方案。

但是有一個例外:如果你在所有的(或者幾乎所有的)int數值上叠代,那麽使用int類型的循環索引的速度大約可以提高一倍。下面是將f函數作用於所有40億個int數值上的慣用法:


//Apply the function f to all four billion int values

int i = Integer.MIN_VALUE;

do {

    f(i);

}while (i++ != Integer.MAX_VALUE);

該謎題對語言設計者的教訓與謎題3相同:可能真的值得去考慮,應該對那些不會在產生溢出時而不拋出異常的算術運算提供支持。同時,可能還值得去考慮,應該對那些在整數值範圍之上進行叠代的循環進行特殊設計,就像許多其他語言所做的那樣。

謎題26:在循環中