Java日常错误及需要注意细节,持续更新......
记录日常工作中一些容易被忽视的错误及细节,持续更新......
一、问题:hashmap<long, string>中,用get(integer key)取不到值
map<long, string> map = new hashmap<long, string>(); map.put(1l, "1"); system.err.println(map.get(1));// null system.err.println(map.get(1l));// 1
1.首先想到long与integer的hashcode方法不同,integer-value long-(int)(value ^ (value >>> 32))
但是!!计算出的hashcode值是相同的,不是问题所在
2.查看hashmap源码:注意加亮部分
先比较key.hash,然后first.key == key || key.equals(first.key)
/** * 先比较key.hash,然后first.key == key || key.equals(first.key) */ final node<k,v> getnode(int hash, object key) { node<k,v>[] tab; node<k,v> first, e; int n; k k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof treenode) return ((treenode<k,v>)first).gettreenode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
先看first.key == key:"=="比较地址值,l是long cache[]中的1,o是integer cache[]中的1,false
long l = 1l; object o = 1; system.err.println(l == o);// false // 反编译后: long l = long.valueof(1l); object o = integer.valueof(1); system.err.println(l == o);
然后看key.equals(first.key):先检查类型,false
//long的equals方法 public boolean equals(object obj) { if (obj instanceof long) { return value == ((long)obj).longvalue(); } return false; }
引发新的问题:为什么这个是true?——反编译解决
long l = 1l; system.err.println(l == 1);// true // 反编译后: long l = long.valueof(1l); system.err.println(l.longvalue() == 1l);//编译器直接将1转成1l
二、两个值相等的integer不“==”
integer c = 99999; integer d = 99999; system.out.println(c == d);// false
integer c = 99999;// 反编译:integer c = integer.valueof(99999);
查看integer源码:
-128 <= i <= 127时,直接在integer cache[]中取;否则,new integer(i)
public static integer valueof(int i) { if (i >= integercache.low && i <= integercache.high) return integercache.cache[i + (-integercache.low)]; return new integer(i); }
结论:
int a = 99999; int b = 99999; system.err.println(a == b);// true integer c = 99999; integer d = 99999; system.out.println(c == d);// false integer e = 127; integer f = 127; system.out.println(e == f);// true
三、list.remove()方法调用错误
注意list两个remove方法,remove(int index) remove(object o)
public static void main(string[] args) { list<integer> list = new linkedlist<integer>(); for (int i = 0; i < 9999999; i++) { list.add(i); } // remove(int index) long before = system.currenttimemillis(); int i = 8888888; list.remove(i); long after = system.currenttimemillis(); system.err.println("index=" + (after - before));// 6ms // remove(object o) long before = system.currenttimemillis(); integer i = 8888888; list.remove(i); long after = system.currenttimemillis(); system.err.println("object=" + (after - before));// 96ms }
四、三目运算符与自动拆装箱
map<string,boolean> map = new hashmap<string, boolean>(); boolean b = (map!=null ? map.get("test") : false);// exception in thread "main" java.lang.nullpointerexception
查问题:
nullpointerexception找不出原因
反编译看: ((boolean)map.get("test")) == null
hashmap map = new hashmap(); boolean boolean1 = boolean.valueof(map == null ? false : ((boolean)map.get("test")).booleanvalue());
结论:
三目运算符的语法规范,参见 。
三目运算符 当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作。
以后注意:
1、保证三目运算符的第二第三位操作数都为对象类型
map<string,boolean> map = new hashmap<string, boolean>(); boolean b = (map!=null ? map.get("test") : boolean.false);
2、自动拆装箱问题
integer integer = 1; // 装箱 integer integer=integer.valueof(1); new integer() int i = integer; // 拆箱 int i=integer.intvalue();
1)包装对象的数值比较,不能简单的使用==(只有-128到127之间integercache内的数字可以,但是这个范围之外还是需要使用equals
比较)。
2)自动拆箱,如果包装类对象为null,那么自动拆箱时就有可能抛出npe。
3)如果一个for循环中有大量装箱操作,会浪费很多资源。