【Java】20211019_Java 集合 _HashMap(JDK1.7)
【Java】20211019_Java集合_HashMap(JDK1.7)
一、链表
链表是不连续的内存空间,单向链表之间通过持有下一节点的内存地址来指向。
二、HashMap的底层结构
JDK1.7的HashMap底层结构(见代码块2-1):数组+链表;
[^_代码块2-1]
/**
* The table, resized as necessary. Length MUST Always be a power of two.
* 根据需要对数组table扩容,但需要保证数组的长度始终是2的幂次。
*/
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
数组的标志——"[]"有了,再看作为链表结构的Entry<K,V>(见代码块2-2):
[^_代码块2-2]
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next; // 持有下一Entry节点的地址
int hash;
}
每个Entry节点中都持有下一节点的地址,一个接一个形成链表结构。
三、为什么是【数组+链表】
个人猜想:数组查询快,链表增删强,强强来联手,两者得益彰。
四、从构造方法开始,看看码
在JDK1.7,HashMap提供了四种构造,这里只贴其中三种的代码(见代码块4-1)分析。
[^_代码块4-1]
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
* 构造一个还没有放入任何元素的HashMap,其初始化容量为16,默认的加载因子0.75
*/
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();
}
如果在创建HashMap时,没有指定初始化容量,就会默认初始化一个容量为16的HashMap。指定了容量,那就按指定的来,不过在put方法时还是会根据这个容量做调整。
在看put方法前,先看一下别的方法,省得一会儿跳来跳去看代码晕乎了。
inflateTable方法(见代码块4-2):
[^_代码块4-2]
/**
* Inflates the table.
*/
private void inflateTable(int toSize) {
// Find a power of 2 >= toSize
(1) int capacity = roundUpToPowerOf2(toSize);
(2) threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
(3) table = new HashMap.Entry[capacity];
(4) initHashSeedAsNeeded(capacity);
}
static final int MAXIMUM_CAPACITY = 1 << 30; // 1073741824->不用数了,10个亿多
private static int roundUpToPowerOf2(int number) {
// assert number >= 0 : "number must be non-negative";
return number >= MAXIMUM_CAPACITY
? MAXIMUM_CAPACITY
: (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}
(1)roundUpToPowerOf2方法整体看下来,【较大概率】只会发生一件事:将传入的任意值,通过运算,返回一个【大于等于】传入值又在数值上最接近传入值的2次幂数。
(2)这行代码【较大概率】会将上一步的返回值与0.75做乘法,将得到的值赋给threashold——中文翻译:阈值。
(3)构造一个容量为roundUpToPowerOf2方法返回值的新数组替换掉HashMap中原有数组。
(4)略。
put方法(见下图:)
努力让自己不那么菜
明天就1024了,本来今天网络冲浪的时候微信群里发现了个GIF,想分享出来。
但因为我不知道微信咋保存,所以去B站扒了个相同的视频下来想视频转GIF,但是喵了个咪的现在的工具就想着收钱,干活的时候网速飞慢,要钱的时候伸手贼快,我又比较穷,就面向搜索引擎找了个代码跑了下把GIF给生成了。
欲速则不达,每天进步一点点。
居然0回复,打破这个僵局
提问:如何限制HashMap的元素数量?比如在put了100个元素之后,再插入新的元素,就把最旧的元素删除掉
mark
坐等答案😭
考虑value 有个时间戳或者自增id啥的, 进行排序! 我是这么想的... 断了!
目前我是遇到了这个问题,还没时间构思,重写方法应该可以解决问题
限制了HashMap的元素数量,感觉就没意义。比如你限制了100个元素,那能保证这100个元素很均匀的分布在数组上吗?分布不了
那么使用什么代替是最优解呢?List的查找速度跟HashMap完全不是一个级别的
现在又有需求使用hashmap,但是不能无限元素数量,这样的话会OOM
元素带上时间戳,每次put操作前,检测size,size没问题直接put,size有有问题讨论是否属于覆盖数据,覆盖数据直接put,非覆盖数据根据时间戳先remove再put。
问题在于,如何删除第一个元素?假设Key不是按0-100排序的,而是英文单词,那么是不是每次remove都要遍历一下呢(想到解决方案了,用iterator.next()直接把指针0位置的元素拿出来)
或者再额外搞个队列来记录key的put顺序就行了撒,反正队列就是先进先出的正好符合这个需求