本文旨在分享刷LeetCode 657. Robot Return to Origin时遇到的大坑:Java相同类型的包装类对象之间值的比较问题。
题目一个关于二维平面的机器人能否返回原点的问题:
There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its moves, judge if this robot ends up at (0, 0)after it completes its moves.
The move sequence is represented by a string, and the character moves[i] represents its ith move. Valid moves are R (right), L (left), U (up), and D (down). If the robot returns to the origin after it finishes all of its moves, return true. Otherwise, return false.
Note: The way that the robot is "facing" is irrelevant. "R" will always make the robot move to the right once, "L" will always make it move left, etc. Also, assume that the magnitude of the robot's movement is the same for each move.
思路也很简单,就是判断上下左右移动的步数,如果向上等于向下,向左等于向右,就可以回到原点,否则就不可以。利用HashMap记录一下U、D、R、L的个数,然后判断是否相等即可。
错误的解答如下:
class Solution {
public boolean judgeCircle(String moves) {
System.out.println(moves.length());
if(moves.length()==0){
return true;
}
if(moves.length()%2!=0){
return false;
}
HashMap<Character,Integer> map = new HashMap<Character,Integer>();
for(int i=0;i<moves.length();i++){
if(!map.containsKey(moves.charAt(i))){
map.put(moves.charAt(i),1);
}else{
map.put(moves.charAt(i),map.get(moves.charAt(i))+1);
}
}
System.out.println("U="+(map.get('U')));
System.out.println("D="+(map.get('D')));
System.out.println("R="+(map.get('R')));
System.out.println("L="+(map.get('L')));
System.out.println(map.get('U')==map.get('D'));
System.out.println(map.get('R')==map.get('L'));
if(map.get('U')==map.get('D') && map.get('R')==map.get('L')){
return true;
}else{
return false;
}
}
}
提交以后显示不正确,输出打印结果如下:
感觉莫名其妙,加入以下输出信息以后,确定是两个Integer变量比较的时候出问题了。
Integer类型的数据在-128~127范围内用==和equals比较的结果是一样的,超出此范围才会出现差异。
为什么会出现这种情况呢?Integer是int对应的包装类,它有一个int类型的字段存储数据,并且提供了基本操作,比如数学运算、int和字符串之间转换等。在Java 5中,引入了自动装箱和自动拆箱功能(boxing/unboxing),Java可以根据上下文,自动进行转换,极大地简化了相关编程。
关于Integer的值缓存,这涉及Java 5中另一个改进。构建Integer对象的传统方式是直接调用构造器,直接new一个对象。但是根据实践,我们发现大部分数据操作都是集中在有限的、较小的数值范围,因而,在Java 5中新增了静态工厂方法valueOf,在调用它的时候会利用一个缓存机制,带来了明显的性能改进。按照Javadoc,这个值默认缓存是-128到127之间。
这确实是一个大坑,正如《阿里巴巴Java开发手册》中强制规约的一样:所有相同类型的包装类对象之间值的比较,全部使用equals方法。
修改以后的代码可以获得通过,如下:
class Solution {
public boolean judgeCircle(String moves) {
if(moves.length()==0){
return true;
}
if(moves.length()%2!=0){
return false;
}
HashMap<Character,Integer> map = new HashMap<Character,Integer>();
for(int i=0;i<moves.length();i++){
if(!map.containsKey(moves.charAt(i))){
map.put(moves.charAt(i),1);
}else{
map.put(moves.charAt(i),map.get(moves.charAt(i))+1);
}
}
if(!map.containsKey('U')){
map.put('U',0);
}
if(!map.containsKey('D')){
map.put('D',0);
}
if(!map.containsKey('R')){
map.put('R',0);
}
if(!map.containsKey('L')){
map.put('L',0);
}
if(map.get('U').equals(map.get('D')) && map.get('R').equals(map.get('L'))){
return true;
}
return false;
}
}
其实这道题还有稍微简单点的解法,可以通过判断机器人在水平和垂直方向有没有相对移动,代码如下:
class Solution {
public boolean judgeCircle(String moves) {
System.out.println(moves.length());
if(moves.length()==0){
return true;
}
if(moves.length()%2!=0){
return false;
}
int h = 0;
int v = 0;
for(int i=0;i<moves.length();i++){
char c = moves.charAt(i);
switch(c) {
case 'U':
v=v+1;
break;
case 'D':
v=v-1;
break;
case 'R':
h=h+1;
break;
case 'L':
h=h-1;
break;
}
}
return h==0 && v==0;
}
}