前述:
a、transient关键字用于可序列化的类中,标示该字段不需要被序列化。也就不能传输。例:HashMap中的entrySet字段
b、哈希表(散列表) 哈希算法+数组
1、HashMap(哈希表)
非线程安全
数组+单链表 —> HashMapEntry<K,V>[] table
扩容策略——>加倍
最大容量——>MAXIMUM_CAPACITY = 1 <<30
最小容量——>MINIMUM_CAPACITY = 4
初始化默认容量——> 4 >>> 1 即 2
HashMap的容量总是2^N形式,当指定容量时(3),HashMap会自动寻找大于等于指定容量并符合2^N形式的值,也就是4。
扩容因子 默认0.75,由硬件写入,不可改。当使用的桶的数量等于总桶数的0.75时,便开始扩容
key值的Hash值计算——>Collections.secondaryHash(key)
private static int secondaryHash(int h) {
// Spread bits to regularize both segment and index locations,
// using variant of single-word Wang/Jenkins hash.
h += (h << 15) ^ 0xffffcd7d;
h ^= (h >>> 10);
h += (h << 3);
h ^= (h >>> 6);
h += (h << 2) + (h << 14);
return h ^ (h >>> 16);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
插入值所在数组位置计算——>hash & (table.length -1)
HashMapEntry——>新元素 单链表
插入策略:1、根据key计算Hash值。
2、hash值与数组长度计算插入到数组的位置。
3、如果插入的位置为空,则新建HashMapEntry。
4、如果插入位置不为空,遍历已有hashMapEntry,遇到相同的key就替换value。
如果没有相同的key,则添加新的hashMapEntry到单链表的头部。(注:如果key为空,则以null为key)
遍历策略:1、查找第一个有值的数组位置作为起始位置。
2、遍历这个位置的单链表。
3、知道链表尾部为null。
4、查找下一个有值的数组位置。
5、直到数组的最后一个位置。
2、LinkedHashMap
继承HashMap 非线程安全
数组+双向链表 ——> LinkedEntry<K,V>[] table
复写了addNewEntry方法和get方法
因为是双向链表,所以LinkedHashMap就可以维护整个链表的顺序
支持两种遍历顺序(按插入顺序遍历、按访问顺序遍历),默认是按照插入顺序遍历
其内部持有的header对象(LinkedEntry)即最新插入的元素,header.prv元素即最久的元素
3、LruCache (不能算是一种数据结构)
基于LinkedHashMap实现
accessOrder为true,即按照访问顺序遍历
每访问一次一个元素,这个元素会变成header(最新的元素),同时改变header的pre元素和next元素
关键方法trimToSize(int maxSize) 每添加一个新的Entry时都会调用一次此方法,判定当前容量是否超过最大容量。如果超过了最大容量,就会循环删除最久添加的Entry,直接当前容量小于等于最大容量。
4、List
内部实现为数组Array
ArrayList默认初始容量为10
扩容策略:如果初始容量小于12/2,扩容后容量为初始容量+12。如果初始容量大于等于12/2,扩容后容量为初始容量的2倍。
5、Tree
内部实现为节点node(parent left right)
7、链表
逻辑上的线性,通过指针或引用把数据块连接起来