我找到了一个非常神奇的东西,简单的代码如下:
public class Demo{ public static void main(String[] args){ HashMapmap = new HashMap (); map.put("a", "aa"); System.out.println("end"); } }
在调用之后
HashMapmap = new HashMap ();
字段变量entrySet不为null,也就是说它已经初始化.
那么这是我的第一个问题,什么时候初始化了entrySet?似乎相关的代码应该在HashMap的构造中,但下面是这个构造函数的源代码
public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
它似乎不存在初始化entrySet的代码.
事情还在继续.在调用之后
map.put("a","aa")
字段变量表和entrySet的内容如下图所示. 那么这是我的第二个问题:将此值添加到entrySet中时?似乎应该是put方法实现这些东西.以下是放方法.
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
它调用putVal方法,下面是putVal的一些代码
final V putVal(...) { .... tab[i] = newNode(hash, key, value, null); .... ++modCount;//after invoke this the entrySet is still empty if (++size > threshold) resize();//this has not been executed afterNodeInsertion(evict);//I debug several times, sometimes before invoke this the entrySet has an Element and sometimes return null; }
在调用之后
++modCount;
在调用之前,entrySet为空
afterNodeInsertion(evict);
entrySet有一个元素.但似乎这两行之间的代码与entrySet没有任何关系.我想也许有几个线程操作entrySet然后我用jvm_ti编写一个小工具来打印threadID,它调用java.util包下面的类,发现只有一个线程.
那我想念的是什么?调试过程中是否存在问题?希望我清楚地描述我的问题,一切都会被欣赏.
add: 我的java版本是1.8.0_77,eclipse版本是4.6.1和4.5.1
是你的调试器愚弄你.调试器视图调用toString()
实际调用的内容entrySet()
(请参阅参考资料AbstractMap.toString()
).这就是为什么entrySet
当你看到它时已经初始化了.
如果你通过反射工具查看,例如使用以下代码:
HashMapmap = new HashMap<>(); Field entrySetField = HashMap.class.getDeclaredField("entrySet"); entrySetField.setAccessible(true); Object entrySet = entrySetField.get(map); System.out.println("entrySet = " + entrySet); System.out.println("map.toString() = " + map.toString()); entrySet = entrySetField.get(map); System.out.println("entrySet = " + entrySet);
你得到以下输出:
entrySet = null map.toString() = {} entrySet = []
正如您所看到的那样:null
如果没有toString()
被调用并且在它之后被初始化,则实际上仍然是entrySet .
这同样适用于您的第二个问题.如果你"反思地"看待这些值:
// Starting from where my entrySet is still null map.put("key", "value"); entrySet = entrySetField.get(map); System.out.println("entrySet = " + entrySet);
你得到了,如预期的那样:
entrySet = null