Java坑人面試題系列: 比對while與for循環(中級難度)

Java Magazine上面有一個專門坑人的面試題系列: https://blogs.oracle.com/javamagazine/quiz-2

這些問題的設計宗旨,主要是測試面試者對Java語言的瞭解程度,而不是爲了用彎彎繞繞的手段把面試者搞蒙。

如果你看過往期的問題,就會發現每一個都不簡單。

這些試題模擬了認證考試中的一些難題。 而 “中級(intermediate)” 和 “高級(advanced)” 指的是試題難度,而不是說這些知識本身很深。 一般來說,“高級”問題會稍微難一點。

請回答一個問題: 在使用循環進行遍歷時,你更喜歡使用哪種循環: for 還是 while

問題(中級難度)

問題的目標是比較Java中各種循環的語法。

假設需要編寫程序,在控制檯引導用戶輸入,並進行響應, 執行的步驟大致是這樣的:

  1. 輸出提示和引導信息;
  2. 讀取用戶輸入的指令;
  3. 如果指令爲 quit, 則退出程序;如果輸入其他指令,則執行對應的邏輯,完成後跳轉到第1步。

要實現上述功能,使用哪種語法和方式最好? 請選擇:

  • A、 使用 for 循環和 break 語句;
  • B、 使用簡寫/增強的 for 循環;
  • C、 使用 while 循環和 continue 語句;
  • D、 使用 do/while 循環;

答案和解析

這個問題要求做一個最佳選擇,一般不會讓你選擇多個答案。

有兩個關鍵的地方,可以幫助我們進行判斷。
一、 步驟1和步驟2都必須至少執行一次; 也就是說,至少要讀取一個命令之後,代碼纔會退出。
二、 在讀取命令之前,不可能做出退出程序的決定。

讓我們考慮一下,如果使用 【while循環】需要怎麼處理。
首先 while 循環在入口處執行條件判斷; 該測試是在循環體執行之前執行的。
所以,如果使用while 循環來進行控制,一種可能的方式,是在循環開始之前打印提示並讀取命令,僞代碼所下所示:

Issue prompt                  // 打印提示引導信息
Read command                  // 讀取用戶指令
While (command is not "quit") // 判斷如果命令不是quit才進入循環
  Execute the command         // 執行命令...
  ...

可能初看起來沒有什麼問題,但我們還需要提示並並讀取後續命令。
每次的循環操作中都需要執行這個操作的相關代碼。
也就是說會有代碼重複。僞代碼示例如下:

Issue prompt                  // 打印提示引導信息
Read command                  // 讀取用戶指令
While (command is not "quit") // 判斷如果命令不是quit才進入循環
  Execute the command         // 執行命令
  Issue prompt                // 打印提示引導信息; 【重複代碼】
  Read command                // 讀取用戶指令; 【重複代碼】
End-while                     // while循環結束

重複代碼不僅看起來不爽,在維護過程中也容易出錯,對於追求卓越的程序員來說是不好的習慣。
另一種方式,是在循環之前將命令設置爲某個虛擬值,並確保在碰到虛擬命令時不會執行任何操作。
這能避免重複代碼,但也會帶來額外的複雜性,以後閱讀代碼的人可能難以理解,因爲必須使用特殊的值來進行“hack”。

這種情景直接使用 while 循環肯定不太合理,下面我們再來看看其他方案。

接下來,看看如果使用 【for循環】 怎麼處理。
for 循環實際上是另一種方式的 while 循環, 目的是爲了在某些場景下編程更加直觀和容易理解。
常見的兩種場景是:

  • 一、 將循環中使用的一些變量進行聲明和初始化。
  • 二、 在迭代過程中更新某些變化量。

從根本上來說, forwhile 循環加一些額外部分形成的。
在這個場景中,這些額外部分可能會有用,但仍然會存在重複的 [提示和讀取輸入] 代碼。

然後我們一起來考察【簡寫的for循環】(enhanced for loop,增強的for循環)。
增強的for循環提供了一種簡潔的方式來遍歷可迭代對象(Iterable object,如集合/數組)。
在本文指定的問題中,必須從控制檯讀取輸入命令,當輸入值爲 quit 時循環終止。
雖然也可以創建一個 Iterable 對象來引導提示用戶,並讀取文本內容,如果輸入不是quit時就返回字符串。 如果用戶的輸入是 quit,則終止迭代,但對於這個場景來說,這種實現方式就很複雜了,而且也不直觀。
在這種情況下,似乎不能推薦使用增強的for循環,可以把它留下作爲備選,看看有沒有更好的方式。

剩下的選項是 do/while 循環。 這種方式將決定是否繼續循環的測試條件放在 do/while 結構的末尾,
效果就是 do/while 循環的body至少會被執行一次,因爲必須先執行body之後才能到達測試條件判斷。
也就是循環體是在判斷條件之前執行的。
do/while 主要就是適用於這樣的場景: 可以在循環體中進行引導提示,並讀取命令輸入。
這樣就沒有重複的代碼,確保提示和輸入發生在條件判斷之前,代碼結構會比較乾淨和簡潔。

那麼很明顯, 選項D非常適合這個場景, 因此,它是比其他選項更好的選擇。

通過這些解析,我們最終可以說 選項D是正確答案,而選項A、B和C是不正確的。

注: 選項A中提到使用 break 語句,而選項C提到使用 continue 語句。
雖然使用 forbreak的方式也可以讓循環體強制執行,但這兩種方式的代碼也不會很優雅。

選項A的僞代碼大致如下:

for (;;) {                   // 死循環
    ......                   // 展示引導和提示信息
    String input = ...       // 讀取用戶輸入
    if (input.equals("quit"))
        break;               // 如果是quit則退出循環
    executeCommand(input);   // 否則就執行命令...
}

但依然沒有 do/while 方式直觀和簡潔。

而在 while 循環中使用 continue 則是標準的錯誤答案,你沒法構造出一個可執行的解決方案。

總結

正確的選項是D。

do/while 主要就適用這種至少執行1次的場景。

當然,在某些開源代碼中你會發現只執行一次的代碼使用了 do{ if-break; } while(true); 的方式,這種方式的好處是可以減少if嵌套的深度,必要時進行中斷。 一層一層的嵌套代碼實際上是難以維護和理解的,而抽象爲方法又涉及傳值的問題,所以怎麼方便就怎麼來是比較好的選擇。

相關鏈接

原文鏈接: https://blogs.oracle.com/javamagazine/quiz-intermediate-loop-constructs

發佈了112 篇原創文章 · 獲贊 1704 · 訪問量 530萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章