起因
最近在刷題,有一簡單題 20. 有效的括號
我的作答如下,感覺可以順利通過,卻怎么也通不過:
public boolean isValid(String s) {
if (s.length() % 2 != 0) return false;
char[][] blacks = new char[][]{{'(', ')'}, {'[', ']'}, {'{', '}'}};
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == blacks[0][0]) stack.push(blacks[0][1]);
else if (c == blacks[1][0]) stack.push(blacks[1][1]);
else if (c == blacks[2][0]) stack.push(blacks[2][1]);
else if (c == blacks[0][1]) if (stack.isEmpty() || stack.pop() != blacks[0][1]) return false;
else if (c == blacks[1][1]) if (stack.isEmpty() || stack.pop() != blacks[1][1]) return false;
else if (c == blacks[2][1]) if (stack.isEmpty() || stack.pop() != blacks[2][1]) return false;
}
return stack.isEmpty();
}
輸入是:
()[]{}
最終輸出false
打印棧中剩余元素:
],}
怎么會這樣呢?這和平時的直覺完全不一樣啊。
最開始猜測可能是包裝類對象地址不同的問題,但是一看明顯又不是,一個是包裝類,一個是基本數據類型,會自動拆裝箱,不是會是這個問題。
於是猜測可能是else if
后面沒有加大括號的原因,加上大括號順利通過。
之前以為else if
只要是一句話就行,以;
算是結束,但是顯然,就這題而言並不是這樣的。
深入探究
為了深入探究為什么會這樣,查看java字節碼如下,截取第一個else if
對應的字節碼如下:
L10
LINENUMBER 80 L10 // 第80行
FRAME SAME
ILOAD 7
ALOAD 2
ICONST_0
AALOAD
ICONST_1
CALOAD
IF_ICMPNE L8 // 比較棧頂兩個int類型數值大小,當前者不等於后者時跳轉到L8,L8是for循環開始的地方
ALOAD 3
INVOKEVIRTUAL java/util/Stack.isEmpty ()Z
IFNE L11 // 如果棧空,到L11,L11是返回false
ALOAD 3
INVOKEVIRTUAL java/util/Stack.pop ()Ljava/lang/Object;
CHECKCAST java/lang/Character
ALOAD 2
ICONST_0
AALOAD
ICONST_1
CALOAD
INVOKESTATIC java/lang/Character.valueOf (C)Ljava/lang/Character;
INVOKEVIRTUAL java/lang/Character.equals (Ljava/lang/Object;)Z // true 1, false,0
IFNE L12 // 當棧頂int類型數值不等於0時跳轉 L12,即, 兩個元素相等,跳轉下一個if條件
L11 // 這段邏輯就是 return false;
FRAME SAME
ICONST_0
IRETURN
從字節碼可以看出來,第一個else if
其邏輯會變成這樣:
- 第一個條件判斷:如果不相等,直接開始下一輪循環;如果相等,那么會進入
if
判斷: if判斷
:如果棧空返回false
,如果棧不空,那么判斷棧頂元素和當前元素是否相等:- 如果相等進入下一個條件判斷,如果不相等,那么返回
false
這邏輯很詭異:
對於第1條,我們希望,第一個條件判斷是如果不相等進入到下一個條件判斷,但是這里直接進入下一輪循環了,后邊的代碼直接跳過了...
對於第3條,如果元素相等,那么應該是進入下一輪循環,這里竟然是走到下一個條件判斷...
所以根據這樣的邏輯,輸入:
()[]{}
那么一共六輪循環:
)
入棧- 第一個
elif
相等,進入if
,棧不空,棧頂元素()
順利出棧)和當前元素相等,那么進入下一輪條件判斷,第二個else-if
判斷因為不相等直接到下一輪循環 ]
入棧- 第一個
elif
不相等,直接進入下一輪循環 }
入棧- 第一個
elif
不相等,直接進入下一輪循環
所以最終棧內只剩下元素:
]
和 }
經過debug,和梳理的邏輯一致。
總結
平時在工作中寫代碼時,我都會加上大括號,因為這是java規范所要求的,一是便於閱讀,而是后面新增代碼時,不容易出錯。
但是刷題時,就是怎么看起來簡潔怎么來,沒想到if-else
不用大括號還會遇到這樣的坑...
最后:如果使用到if-else
的條件判斷,最好還是加上大括號,特別是后邊跟着比較復雜的表達式時。