起因
最近在刷题,有一简单题 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
的条件判断,最好还是加上大括号,特别是后边跟着比较复杂的表达式时。