我发现了一个非常神奇的东西,简单的代码如下:
public class Demo{
public static void main(String[] args){
HashMap<String,String> map = new HashMap<String,String>();
map.put("a", "aa");
System.out.println("end");
}
}
调用后
HashMap<String,String> map = new HashMap<String,String>();
字段变量 entrySet 不是 null ,也就是说它已经初始化。
当 entrySet 初始化时,这是我的第一个问题? 似乎相关的代码应该在 HashMap 的构造中,但下面是这个构造函数的源代码
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
似乎不存在初始化 entrySet 的代码。
事情还在继续。 在调用
之后map.put("a","aa")
字段变量 table 和 entrySet 的内容如下所示。 那么这是我的第二个问题:将此值添加到entrySet中? 它似乎应该是 put 方法实现这些东西。 以下是 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 编写一个小工具来打印调用下面的类 java.util 并找到只有一个主题。
那我错过了什么?调试过程中是否存在问题?希望我清楚地描述我的问题,一切都会被欣赏。
添加:我的java版本是 1.8.0_77 ,eclipse版本是 4.6.1 和 4.5.1
答案 0 :(得分:6)
这是你的调试器愚弄你。调试器视图调用toString()
,实际上调用entrySet()
(请参阅AbstractMap.toString()
)。这就是entrySet
已经初始化的原因,当你看到它时。
如果你通过反射工具查看,例如使用以下代码:
HashMap<String, String> map = 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
被调用并且在它之后被初始化,则entrySet实际上仍为toString()
。
这同样适用于您的第二个问题。如果你“反思地”看这些值:
// Starting from where my entrySet is still null
map.put("key", "value");
entrySet = entrySetField.get(map);
System.out.println("entrySet = " + entrySet);
你得到的,正如所料:
entrySet = null
答案 1 :(得分:1)
快速查看源代码,发现它是懒惰分配的:
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
我想也许有几个线程操作entrySet然后我 用jvm_ti编写一个小工具来打印调用它的threadID 下面的包java.util,并且发现只有一个线程。
不,肯定涉及NO
个线程(除非您明确创建它们)
如果您想轻松调试,请在
transient Set<Map.Entry<K,V>> entrySet;
在HashMap中。
答案 2 :(得分:0)
entrySet只是一个EntrySet实例,没有在其中初始化或添加值的地方,大多数导入函数在for-each中都用作迭代器,因为它扩展了Iterable