首页 > 代码库 > HashMap和HashSet的源代码分析
HashMap和HashSet的源代码分析
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认的扩容倍数 static final Entry<?,?>[] EMPTY_TABLE = {}; //就比较用的 transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;//Entry数组,存放数据的地方 int threshold; //初始化长度,可以配置,默认长16 final float loadFactor; //可自定义扩容倍数
上面的变量会有用到的。HashMap的一些主要变量,或常量。先来讲HashMap,HashSet一下就懂了。
HashMap有三个构造函数。
public HashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); }
public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); }
public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; threshold = initialCapacity; init(); }
new HashMap的时候只是给变量初始化了。但是他存数据的地方table还是{}的。到put的时候table才有长度。
public V put(K key, V value) { //判断table是否为{},是初始化数组 if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key);//得到key的hash值 int i = indexFor(hash, table.length);//通过hash值找到他应该放到数组中的位置 //判断该位置是否已经有值了。如果有值,判断他们的键是否相同,相同不保存。 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; } private void inflateTable(int toSize) { // Find a power of 2 >= toSize int capacity = roundUpToPowerOf2(toSize); threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); table = new Entry[capacity];//创建了长度为capacity的Entry数组,默认长度为16 initHashSeedAsNeeded(capacity); } void addEntry(int hash, K key, V value, int bucketIndex) { //判断数组已经添加的个数是否>=定义的阀值(默认是16*0.75=12),而且需要存放的位置已经有值了,就扩容2倍。16*2 if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length);//重新得到这个值应该放的位置。 } createEntry(hash, key, value, bucketIndex); } //添加进数组,并且数量+1 void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<>(hash, key, value, e); size++; }
HashMap就这么简单的分析完了。
总结:HashMap可以认为是默认长度为16,阀值为12的一个entry数组。想起key,value,我们应该会想起entry的key,value吧。他存值通过hash定位,但是不仅仅是hash,还有equals方法。当添加值的数量大于阀值时,扩容原来长度的2倍,阀值也扩大2倍。扩容之后再重新定位要存的值。例如我们要用hashmap保存user的时候,user有username,当两个用户的username相同的时候,我们认为是同一个人,就要重写user的hashcode和equals方法。
再来看HashSet.
public HashSet() { map = new HashMap<>(); }
一个构造函数就知道了。
HashMap和HashSet的源代码分析
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。